riff: Basic support for extracting RDIB images
[deark.git] / modules / apple2-dsk.c
blob3f80ec87065d33189e257210a51b78cf0374a924
1 // This file is part of Deark.
2 // Copyright (C) 2018 Jason Summers
3 // See the file COPYING for terms of use.
5 // Apple II disk image formats, etc.
7 #include <deark-private.h>
8 #include <deark-fmtutil.h>
9 DE_DECLARE_MODULE(de_module_woz);
11 #define CODE_INFO 0x494e464fU
12 #define CODE_META 0x4d455441U
13 #define CODE_TMAP 0x544d4150U
14 #define CODE_TRKS 0x54524b53U
15 #define CODE_WRIT 0x57524954U
17 typedef struct localctx_struct {
18 u8 wozver;
19 } lctx;
21 static const char *get_woz_disk_type_name(u8 t)
23 switch(t) {
24 case 1: return "5.25";
25 case 2: return "3.5";
27 return "?";
30 static void do_woz_INFO(deark *c, struct de_iffctx *ictx,
31 const struct de_iffchunkctx *chunkctx)
33 u8 b;
34 i64 n;
35 i64 pos = chunkctx->dpos;
36 lctx *d = ictx->userdata;
37 de_ucstring *s = NULL;
39 if(chunkctx->dlen<37) return;
40 b = dbuf_getbyte_p(ictx->f, &pos);
41 de_dbg(c, "INFO chunk version: %d", (int)b);
42 b = dbuf_getbyte_p(ictx->f, &pos);
43 de_dbg(c, "disk type: %d (%s)", (int)b, get_woz_disk_type_name(b));
44 b = dbuf_getbyte_p(ictx->f, &pos);
45 de_dbg(c, "write protected: %d", (int)b);
46 b = dbuf_getbyte_p(ictx->f, &pos);
47 de_dbg(c, "synchronized: %d", (int)b);
48 b = dbuf_getbyte_p(ictx->f, &pos);
49 de_dbg(c, "cleaned: %d", (int)b);
51 s = ucstring_create(c);
52 dbuf_read_to_ucstring(ictx->f, pos, 32, s, 0, DE_ENCODING_UTF8);
53 ucstring_strip_trailing_spaces(s);
54 de_dbg(c, "creator: \"%s\"", ucstring_getpsz(s));
55 pos += 32;
57 if(d->wozver<'2') goto done;
59 b = dbuf_getbyte_p(ictx->f, &pos);
60 de_dbg(c, "disk sides: %d", (int)b);
61 b = dbuf_getbyte_p(ictx->f, &pos);
62 de_dbg(c, "boot sector format: %d", (int)b);
63 b = dbuf_getbyte_p(ictx->f, &pos);
64 de_dbg(c, "optimal bit timing: %d", (int)b);
65 n = dbuf_getu16le_p(ictx->f, &pos);
66 de_dbg(c, "compatible hardware: %d", (int)n);
67 n = dbuf_getu16le_p(ictx->f, &pos);
68 de_dbg(c, "required RAM: %dK", (int)n);
69 n = dbuf_getu16le_p(ictx->f, &pos);
70 de_dbg(c, "largest track: %d blocks", (int)n);
72 done:
73 ucstring_destroy(s);
76 static void do_woz_print_metadata_item(deark *c, de_ucstring *name, de_ucstring *val)
78 if(name->len==0 && val->len==0) return;
79 de_dbg(c, "item: \"%s\" = \"%s\"",
80 ucstring_getpsz_d(name),
81 ucstring_getpsz_d(val));
84 static void do_woz_META(deark *c, struct de_iffctx *ictx,
85 const struct de_iffchunkctx *chunkctx)
87 i64 k;
88 int reading_val;
89 de_ucstring *s = NULL;
90 de_ucstring *name = NULL;
91 de_ucstring *val = NULL;
93 // Read the entire metadata string.
94 s = ucstring_create(c);
95 dbuf_read_to_ucstring_n(ictx->f, chunkctx->dpos, chunkctx->dlen,
96 65536, s, 0, DE_ENCODING_UTF8);
98 // Parse out the individual metadata items
99 name = ucstring_create(c);
100 val = ucstring_create(c);
101 reading_val = 0;
103 for(k=0; k<s->len; k++) {
104 i32 ch = s->str[k];
106 if(ch==0x0a) { // End of item
107 do_woz_print_metadata_item(c, name, val);
108 ucstring_empty(name);
109 ucstring_empty(val);
110 reading_val = 0;
112 else if(ch==0x09 && !reading_val) { // Name/value separator
113 reading_val = 1;
115 else { // A non-special character
116 if(reading_val) {
117 ucstring_append_char(val, ch);
119 else {
120 ucstring_append_char(name, ch);
124 do_woz_print_metadata_item(c, name, val);
126 ucstring_destroy(s);
127 ucstring_destroy(name);
128 ucstring_destroy(val);
131 static int my_preprocess_woz_chunk_fn(deark *c, struct de_iffctx *ictx)
133 const char *name = NULL;
135 switch(ictx->chunkctx->chunk4cc.id) {
136 case CODE_TMAP: name = "track map"; break;
137 case CODE_TRKS: name = "data for tracks"; break;
138 case CODE_META: name = "metadata"; break;
139 case CODE_WRIT: name = "disk writing instructions"; break;
142 if(name) {
143 ictx->chunkctx->chunk_name = name;
145 return 1;
148 static int my_woz_chunk_handler(deark *c, struct de_iffctx *ictx)
150 // Always set this, because we never want the IFF parser to try to handle
151 // a chunk itself.
152 ictx->handled = 1;
154 switch(ictx->chunkctx->chunk4cc.id) {
155 case CODE_INFO:
156 do_woz_INFO(c, ictx, ictx->chunkctx);
157 break;
158 case CODE_META:
159 do_woz_META(c, ictx, ictx->chunkctx);
162 return 1;
165 static void de_run_woz(deark *c, de_module_params *mparams)
167 lctx *d = NULL;
168 struct de_iffctx *ictx = NULL;
169 u32 crc;
170 i64 pos = 0;
172 // WOZ has a 12-byte header, then sequence of chunks that are basically the
173 // same format as RIFF.
174 d = de_malloc(c, sizeof(lctx));
175 ictx = de_malloc(c, sizeof(struct de_iffctx));
177 ictx->userdata = (void*)d;
178 ictx->preprocess_chunk_fn = my_preprocess_woz_chunk_fn;
179 ictx->handle_chunk_fn = my_woz_chunk_handler;
180 ictx->f = c->infile;
181 ictx->is_le = 1;
182 ictx->reversed_4cc = 0;
184 if(ictx->f->len<12) goto done;
185 de_dbg(c, "header at %d", (int)pos);
186 de_dbg_indent(c, 1);
187 pos += 3; // "WOZ" part of signature
188 d->wozver = dbuf_getbyte_p(ictx->f, &pos);
189 de_dbg(c, "format version: '%c'", de_byte_to_printable_char(d->wozver));
190 if(d->wozver<'1' || d->wozver>'2') {
191 de_err(c, "Unsupported WOZ format version");
192 goto done;
194 pos += 4; // rest of signature
195 crc = (u32)dbuf_getu32le_p(ictx->f, &pos);
196 de_dbg(c, "crc: 0x%08x", (unsigned int)crc);
197 de_dbg_indent(c, -1);
199 fmtutil_read_iff_format(c, ictx, pos, ictx->f->len-pos);
201 done:
202 de_free(c, ictx);
203 de_free(c, d);
206 static int de_identify_woz(deark *c)
208 if(dbuf_memcmp(c->infile, 0, "WOZ", 3))
209 return 0;
210 if(dbuf_memcmp(c->infile, 4, "\xff\x0a\x0d\x0a", 4))
211 return 0;
212 return 100;
215 void de_module_woz(deark *c, struct deark_module_info *mi)
217 mi->id = "woz";
218 mi->desc = "WOZ floppy disk image";
219 mi->desc2 = "metadata only";
220 mi->run_fn = de_run_woz;
221 mi->identify_fn = de_identify_woz;