]> git.sur5r.net Git - cc65/blob - libsrc/geos-apple/targetutil/convert.c
Use unique variables for cc65 toolchain.
[cc65] / libsrc / geos-apple / targetutil / convert.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <dirent.h>
6 #include <device.h>
7 #include <dio.h>
8
9 unsigned char info_signature[3] = {3, 21, 63 | 0x80};
10
11 dhandle_t dhandle;
12
13 struct dir_entry_t {
14     struct {
15         unsigned name_length  :4;
16         unsigned storage_type :4;
17     }             storage_length;
18     char          file_name[15];
19     unsigned char file_type;
20     unsigned      key_pointer;
21     unsigned      blocks_used;
22     unsigned char size[3];
23     unsigned long creation;
24     unsigned char version;
25     unsigned char min_version;
26     unsigned char access;
27     unsigned      aux_type;
28     unsigned long last_mod;
29     unsigned      header_pointer;
30 }* dir_entry;
31
32 union {
33     unsigned char bytes[512];
34     struct {
35         unsigned      prev_block;
36         unsigned      next_block;
37         unsigned char entries[1];
38     } content;
39 } dir_block;
40
41 union {
42     unsigned char bytes[512];
43     struct {
44         unsigned char addr_lo[254];
45         unsigned char size_lo[2];
46         unsigned char addr_hi[254];
47         unsigned char size_hi[2];
48     } content;
49 } index_block, master_block, vlir_block;
50
51 union {
52     unsigned char bytes[512];
53     struct {
54         unsigned           reserved;
55         unsigned char      info_block[254];
56         unsigned char      vlir_records[128];
57         struct dir_entry_t dir_entry;
58     } content;
59 } header_block;
60
61
62 static void err_exit(char *operation, unsigned char oserr)
63 {
64     if (oserr) {
65         fprintf(stderr, "%s - err:%02x - %s",
66                 operation, (int)_oserror, _stroserror(_oserror));
67     } else {
68         fprintf(stderr, "%s",
69                 operation);
70     }
71     getchar();
72     exit(EXIT_FAILURE);
73 }
74
75
76 static unsigned get_dir_entry(char* p_name)
77 {
78     char*          d_name;
79     char*          f_name;
80     size_t         f_namelen;
81     DIR*           dir;
82     struct dirent* dirent;
83     unsigned       cur_addr;
84     unsigned char  entry_length;
85     unsigned char  entries_per_block;
86     unsigned char  cur_entry;
87
88     /* Split path name into directory name and file name */
89     f_name = strrchr(p_name, '/');
90     if (f_name) {
91         d_name = p_name;
92         *f_name++ = '\0';
93     } else {
94         d_name = ".";
95         f_name = p_name;
96     }
97     f_namelen = strlen(f_name);
98
99     /* Start with high level functions to get handling
100        of relative path and current drive for free */
101     dir = opendir(d_name);
102     if (!dir) {
103         err_exit("opendir", 1);
104     }
105     dirent = readdir(dir);
106     if (!dirent) {
107         err_exit("readdir", 1);
108     }
109
110     /* Field header_pointer directly follows field last_mod */
111     cur_addr = *(unsigned*)(&dirent->d_mtime.hour + 1);
112
113     dhandle = dio_open(getcurrentdevice());
114     if (!dhandle) {
115         err_exit("dio_open", 1);
116     }
117
118     if (dio_read(dhandle, cur_addr, &dir_block)) {
119         err_exit("dio_read.1", 1);
120     }
121
122     /* Get directory entry infos from directory header */
123     entry_length      = dir_block.bytes[0x23];
124     entries_per_block = dir_block.bytes[0x24];
125
126     /* Skip directory header entry */
127     cur_entry = 1;
128
129     do {
130
131         /* Search for next active directory entry */
132         do {
133
134             /* Check if next directory block is necessary */
135             if (cur_entry == entries_per_block) {
136
137                 /* Check if another directory block is present */
138                 cur_addr = dir_block.content.next_block;
139                 if (!cur_addr) {
140                     _mappederrno(0x46);
141                     err_exit("dio_read.2", 1);
142                 }
143
144                 /* Read next directory block */
145                 if (dio_read(dhandle, cur_addr, &dir_block)) {
146                     err_exit("dio_read.3", 1);
147                 }
148
149                 /* Start with first entry in next block */
150                 cur_entry = 0;
151             }
152
153             /* Compute pointer to current entry */
154             dir_entry = (struct dir_entry_t*)(dir_block.content.entries +
155                                               cur_entry * entry_length);
156
157             /* Switch to next entry */
158             ++cur_entry;
159         } while (!dir_entry->storage_length.storage_type);
160
161     } while (dir_entry->storage_length.name_length != f_namelen ||
162              strncasecmp(dir_entry->file_name, f_name, f_namelen));
163
164     return cur_addr;
165 }
166
167
168 int main(int argc, char* argv[])
169 {
170     char          input[80];
171     char*         p_name;
172     unsigned      dir_addr;
173     unsigned      header_addr;
174     unsigned char index;
175     unsigned long size;
176
177     if (argc > 1) {
178         p_name = argv[1];
179     } else {
180         printf("\n"
181                "Apple GEOS Convert 1.0\n"
182                "----------------------\n"
183                "\n"
184                "Pathname:");
185         p_name = gets(input);
186     }
187
188     dir_addr = get_dir_entry(p_name);
189
190     /* Read index block */
191     if (dio_read(dhandle, dir_entry->key_pointer, &index_block)) {
192         err_exit("dio_read.4", 1);
193     }
194
195     /* First pointer is header block */
196     header_addr = index_block.content.addr_lo[0] |
197                   index_block.content.addr_hi[0] << 8;
198
199     /* Read header block */
200     if (dio_read(dhandle, header_addr, &header_block)) {
201         err_exit("dio_read.5", 1);
202     }
203
204     /* Do some sanity check */
205     for (index = 0; index < sizeof(info_signature); ++index) {
206         if (header_block.content.info_block[index] != info_signature[index]) {
207             err_exit("file signature mismatch", 0);
208         }
209     }
210
211     /* Check ProDOS storage type in directory entry template */
212     if (header_block.content.dir_entry.storage_length.storage_type == 2)
213     {
214
215         /* ProDOS sapling file means GEOS Sequential file*/
216         printf("\nSequential file\n");
217
218         /* Remove header block pointer from pointer list */
219         memmove(&index_block.content.addr_lo[0],
220                 &index_block.content.addr_lo[1], sizeof(index_block.content.addr_lo) - 1);
221         memmove(&index_block.content.addr_hi[0],
222                 &index_block.content.addr_hi[1], sizeof(index_block.content.addr_hi) - 1);
223
224         /* Get file size from ProDOS directory entry template */
225         size = (unsigned long)(header_block.content.dir_entry.size[0])       |
226                (unsigned long)(header_block.content.dir_entry.size[1]) <<  8 |
227                (unsigned long)(header_block.content.dir_entry.size[2]) << 16;
228
229     } else {
230
231         /* ProDOS tree file means GEOS VLIR file */
232         unsigned      vlir_addr;
233         unsigned long vlir_size;
234         unsigned char vlir_blocks;
235         unsigned char record = 0;
236
237         printf("\nVLIR file\n");
238
239         /* Skip header block pointer */
240         index = 1;
241         size  = 0;
242
243         while (1) {
244
245             /* Get next VLIR index pointer from index block */
246             vlir_addr = index_block.content.addr_lo[index] |
247                         index_block.content.addr_hi[index] << 8;
248             ++index;
249
250             /* Check for end of pointer list */
251             if (vlir_addr == 0) {
252                 break;
253             }
254
255             /* Check for empty VLIRs */
256             while (header_block.content.vlir_records[record] == 0xFF) {
257
258                 /* Add empty VLIR index pointer to to master index block */
259                 master_block.content.addr_lo[record] = 0xFF;
260                 master_block.content.addr_hi[record] = 0xFF;
261                 ++record;
262             }
263
264             /* Add VLIR index pointer to master index block */
265             master_block.content.addr_lo[record] = (unsigned char)(vlir_addr     );
266             master_block.content.addr_hi[record] = (unsigned char)(vlir_addr >> 8);
267             ++record;
268
269             /* Read VLIR index block */
270             if (dio_read(dhandle, vlir_addr, &vlir_block)) {
271                 err_exit("dio_read.6", 1);
272             }
273
274             /* Get VLIR size from VLIR index block */
275             vlir_size = (unsigned long)(vlir_block.content.size_lo[1])       |
276                         (unsigned long)(vlir_block.content.size_hi[1]) <<  8 |
277                         (unsigned long)(vlir_block.content.size_lo[0]) << 16 |
278                         (unsigned long)(vlir_block.content.size_hi[0]) << 24;
279
280             printf("VLIR %u size %lu bytes\n", record - 1, vlir_size);
281
282             /* Compute VLIR block size */
283             vlir_blocks = (unsigned char)((vlir_size + 511) / 512);
284
285             /* Copy VLIR block pointers from index block to VLIR index block */
286             memcpy(&vlir_block.content.addr_lo[0],
287                    &index_block.content.addr_lo[index], vlir_blocks);
288             memcpy(&vlir_block.content.addr_hi[0],
289                    &index_block.content.addr_hi[index], vlir_blocks);
290             index += vlir_blocks;
291
292             /* Write back VLIR index block */
293             if (dio_write(dhandle, vlir_addr, &vlir_block)) {
294                 err_exit("dio_write.1", 1);
295             }
296
297             /* Add VLIR size to file size */
298             size += vlir_size;
299         }
300
301         /* Replace (by now completely read) index block with
302            (by now completely created) master index block */
303         index_block = master_block;
304     }
305
306     printf("File size %lu bytes\n\n", size);
307
308     /* Set file size in index block */
309     index_block.content.size_lo[1] = (unsigned char)(size      );
310     index_block.content.size_hi[1] = (unsigned char)(size >>  8);
311     index_block.content.size_lo[0] = (unsigned char)(size >> 16);
312     index_block.content.size_hi[0] = (unsigned char)(size >> 24);
313
314     /* Write index block */
315     if (dio_write(dhandle, dir_entry->key_pointer, &index_block)) {
316         err_exit("dio_write.2", 1);
317     }
318
319     /* Copy selected fields from directory entry template to directory block */
320     dir_entry->storage_length  = header_block.content.dir_entry.storage_length;
321     memcpy(dir_entry->file_name, header_block.content.dir_entry.file_name, 15);
322     dir_entry->file_type       = header_block.content.dir_entry.file_type;
323     dir_entry->size[0]         = (unsigned char)(size      );
324     dir_entry->size[1]         = (unsigned char)(size >>  8);
325     dir_entry->size[2]         = (unsigned char)(size >> 16);
326     dir_entry->creation        = header_block.content.dir_entry.creation;
327     dir_entry->version         = header_block.content.dir_entry.version;
328     dir_entry->min_version     = header_block.content.dir_entry.min_version;
329     dir_entry->aux_type        = header_addr;
330     dir_entry->last_mod        = header_block.content.dir_entry.last_mod;
331
332     /* Write directory block */
333     if (dio_write(dhandle, dir_addr, &dir_block)) {
334         err_exit("dio_write.3", 1);
335     }
336
337     /* We're done */
338     if (dio_close(dhandle)) {
339         err_exit("dio_close", 1);
340     }
341
342     printf("Convert to '%.*s' successful", dir_entry->storage_length.name_length,
343                                            dir_entry->file_name);
344     getchar();
345     return EXIT_SUCCESS;
346 }