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
{
21 static const char *get_woz_disk_type_name(u8 t
)
24 case 1: return "5.25";
30 static void do_woz_INFO(deark
*c
, struct de_iffctx
*ictx
,
31 const struct de_iffchunkctx
*chunkctx
)
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
));
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
);
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
)
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
);
103 for(k
=0; k
<s
->len
; k
++) {
106 if(ch
==0x0a) { // End of item
107 do_woz_print_metadata_item(c
, name
, val
);
108 ucstring_empty(name
);
112 else if(ch
==0x09 && !reading_val
) { // Name/value separator
115 else { // A non-special character
117 ucstring_append_char(val
, ch
);
120 ucstring_append_char(name
, ch
);
124 do_woz_print_metadata_item(c
, name
, val
);
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;
143 ictx
->chunkctx
->chunk_name
= name
;
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
154 switch(ictx
->chunkctx
->chunk4cc
.id
) {
156 do_woz_INFO(c
, ictx
, ictx
->chunkctx
);
159 do_woz_META(c
, ictx
, ictx
->chunkctx
);
165 static void de_run_woz(deark
*c
, de_module_params
*mparams
)
168 struct de_iffctx
*ictx
= NULL
;
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
;
182 ictx
->reversed_4cc
= 0;
184 if(ictx
->f
->len
<12) goto done
;
185 de_dbg(c
, "header at %d", (int)pos
);
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");
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
);
206 static int de_identify_woz(deark
*c
)
208 if(dbuf_memcmp(c
->infile
, 0, "WOZ", 3))
210 if(dbuf_memcmp(c
->infile
, 4, "\xff\x0a\x0d\x0a", 4))
215 void de_module_woz(deark
*c
, struct deark_module_info
*mi
)
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
;