2 // fileio.cpp: File I/O
4 // Part of the WOZ Maker project
6 // (C) 2018 Underground Software
7 // See the README and GPLv3 files for licensing and warranty information
17 static uint32_t crcTable[256] =
19 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
20 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
21 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
22 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
23 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
24 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
25 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
26 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
27 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
28 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
29 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
30 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
31 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
32 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
33 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
34 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
35 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
36 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
37 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
38 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
39 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
40 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
41 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
42 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
43 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
44 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
45 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
46 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
47 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
48 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
49 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
50 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
54 uint32_t CRC32(const uint8_t * data, uint32_t length)
56 uint32_t crc = 0xFFFFFFFF;
58 for(uint32_t i=0; i<length; i++)
59 crc = crcTable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
65 uint8_t * ReadFile(const char * filename, uint32_t * size)
67 FILE * fp = fopen(filename, "rb");
72 fseek(fp, 0, SEEK_END);
74 fseek(fp, 0, SEEK_SET);
76 uint8_t * buffer = (uint8_t *)malloc(*size);
77 fread(buffer, 1, *size, fp);
84 bool LoadA2R(const char * filename)
87 Really, this crap should go into fileio.cpp. !!! FIX !!!
89 static uint8_t a2rHdr[8] = { 0x41, 0x32, 0x52, 0x32, 0xFF, 0x0A, 0x0D, 0x0A };
90 static uint8_t a2rHdr1[8] = { 0x41, 0x32, 0x52, 0x31, 0xFF, 0x0A, 0x0D, 0x0A };
92 if (Global::a2r != NULL)
95 Global::a2r = (A2R *)ReadFile(filename, &Global::a2rSize);
97 if (Global::a2r == NULL)
100 // Sanity check, to see if what we asked for is what we got
101 if (memcmp(Global::a2r->magic1, a2rHdr, 8) != 0)
103 // It's not a v2 A2R file, maybe it's v1?
104 if (memcmp(Global::a2r->magic1, a2rHdr1, 8) == 0)
106 // Bytes 8-F are usually: 01 00 8D 00 A5 01 00 00
107 // First, allocate mem to copy the old version to the new:
108 uint8_t * oldfile = (uint8_t *)Global::a2r;
109 uint8_t * newfile = (uint8_t *)malloc(Global::a2rSize + 36);
110 memcpy(newfile + 52, oldfile + 16, Global::a2rSize - 16);
112 // Make sure creator is set correctly (v1 doesn't have it)
113 memset(Global::a2r->creator, 0x20, 32);
115 // Swap in the new file, free the old one
117 Global::a2r = (A2R *)newfile;
118 Global::a2rSize += 36; // Add in the size of the INFO chunk
120 // Fix up stuff that's different between v1 and v2
121 //printf("A2R Size: %08X\n", Global::a2rSize);
122 //printf("A2R Stream Size: %08X (preswap)\n", Global::a2r->strmSize);
123 SwapBytes32((uint8_t *)&Global::a2r->strmSize);
124 //printf("A2R Stream Size: %08X (postswap)\n", Global::a2r->strmSize);
125 uint32_t dataSize = Uint32LE(Global::a2r->strmSize);
128 while (pos < dataSize)
130 A2RStream * stream = (A2RStream *)(&Global::a2r->data[pos]);
132 if (stream->location == 0xFF)
135 SwapBytes32(stream->dataLength);
136 SwapBytes32(stream->estLoopPoint);
137 pos += 10 + Uint32LE(stream->dataLength);
140 // Change INFO to META & fix size (if any, dunno if it's optional)
141 if (Global::a2rSize > (60 + dataSize))
143 memcpy(&Global::a2r->data[dataSize], "META", 4);
144 SwapBytes32(&Global::a2r->data[dataSize + 4]);
152 // QMessageBox::critical(this, "Bad A2R file", "It may have an .a2r extension, but it isn't a valid A2R file.");
158 // Setup individual stream pointers
159 Global::numStreams = 0;
160 uint8_t * streamPtr = Global::a2r->data;
162 while ((*streamPtr != 0xFF) && (Global::numStreams < 800))
164 Global::stream[Global::numStreams] = (A2RStream *)streamPtr;
165 streamPtr += 10 + Uint32LE(Global::stream[Global::numStreams]->dataLength);
166 Global::numStreams++;
170 Global::metadata = NULL;
172 if (Global::a2rSize > (60 + Uint32LE(Global::a2r->strmSize)))
174 Global::metadata = (Metadata *)((uint8_t *)Global::a2r + (60 + Uint32LE(Global::a2r->strmSize)));
176 // Make sure it's plausible metadata
177 if (memcmp(Global::metadata->metaTag, "META", 4) != 0)
178 Global::metadata = NULL;
180 UnpackMetadata(Global::metadata);
183 // Unpack TMNG & XTMG streams to simplify analysis
184 for(uint32_t i=0; i<Global::numStreams; i++)
186 Global::waveLen[i] = 0;
188 // Don't bother with BITS captures
189 if (Global::stream[i]->captureType == 2)
192 // We skip the first timing byte because it can't tell you what the actual time was when the pulse was seen--it's the time between when the *capture* started and the pulse was seen, not the time between the previous pulse and this one! So that first one is discarded since it's worse than useless; it's misleading.
193 for(uint32_t j=1; j<Uint32LE(Global::stream[i]->dataLength); j++)
195 uint32_t a = Global::stream[i]->data[j];
197 while ((Global::stream[i]->data[j] == 0xFF) || (a < 24))
200 a += Global::stream[i]->data[j];
203 Global::wave[i][Global::waveLen[i]] = a;
204 Global::waveLen[i]++;
208 //ISO-8061 date format
211 time_t t = time(NULL);
212 tm * tmp = gmtime(&t);
213 strftime(buf, sizeof(buf), "%FT%TZ", tmp);
214 printf("Time is: %s\n", buf);
221 bool WriteWOZFile(const char * filename)
223 // See if we can actually write a file; if not, there's no need to check
225 FILE * file = fopen(filename, "wb");
230 // Need to come up with proper numbers here...
231 uint32_t numTrackBlocks = 0;
233 for(uint32_t i=0; i<141; i++)
234 numTrackBlocks += (Global::bStreamLen[i] + 511) / 512;
236 uint32_t wozSize = sizeof(WOZ2) + (numTrackBlocks * 512) + (Global::metadata != NULL ? Global::metadata->metaSize + 8 : 0);
238 WOZ2 * woz = (WOZ2 *)malloc(wozSize);
240 memcpy(woz->magic1, "WOZ2\xFF\x0A\x0D\x0A", 8);
242 memcpy(woz->infoTag, "INFO", 4);
243 woz->infoSize = Uint32LE(60);
244 woz->infoVersion = 1;
246 woz->writeProtected = 1;
247 woz->synchronized = 1;
249 memcpy(woz->creator, "WOZ Maker v1.0.0 ", 32);
251 woz->bootSectorFmt = 1;
252 woz->optimalBitTmg = 32;
253 woz->compatibleHW = Uint16LE(0);
254 woz->requiredRAM = Uint16LE(0);
256 memcpy(woz->tmapTag, "TMAP", 4);
257 woz->tmapSize = Uint32LE(160);
259 for(uint32_t i=0; i<141; i++)
260 woz->tmap[i] = (Global::bStreamLen[i] == 0 ? 0xFF : i);
262 memcpy(woz->trksTag, "TRKS", 4);
263 woz->trksSize = Uint32LE(1280 + (numTrackBlocks * 512));
264 uint16_t startBlock = 0, largestBlock = 0;
266 for(uint32_t i=0; i<141; i++)
268 uint16_t blockLen = (Global::bStreamLen[i] + 511) / 512;
269 memset(&woz->data[startBlock * 512], 0, blockLen * 512);
270 memcpy(&woz->data[startBlock * 512], Global::bStream[i], Global::bStreamLen[i]);
271 woz->track[i].startingBlock = Uint16LE(startBlock + 3);
272 woz->track[i].blockCount = Uint16LE(blockLen);
273 woz->track[i].bitCount = Uint16LE(Global::bStreamLenBits[i]);
274 startBlock += blockLen;
276 if (blockLen > largestBlock)
277 largestBlock = blockLen;
280 woz->largestTrack = Uint16LE(largestBlock);
282 if (Global::metadata != NULL)
283 memcpy(&woz->data[startBlock * 512], Global::metadata, Uint32LE(Global::metadata->metaSize) + 8);
285 woz->crc32 = Uint32LE(CRC32(&woz->infoTag[0], wozSize - 12));
286 fwrite(woz, 1, wozSize, file);
295 void UnpackMetadata(Metadata * data)
298 uint32_t end = Uint32LE(data->metaSize);
300 Global::metaCount = 0;
305 Global::meta[Global::metaCount][i++] = data->data[start];
309 if (data->data[start] == '\x0A')
311 Global::meta[Global::metaCount][i] = 0;
320 uint8_t * GetMetadata(const char * keyword)
322 uint32_t kwLen = strlen(keyword);
324 for(uint8_t i=0; i<Global::metaCount; i++)
326 if ((strlen((char *)Global::meta[i]) >= kwLen)
327 && (memcmp(Global::meta[i], keyword, kwLen) == 0))
329 return &Global::meta[i][kwLen];
337 uint16_t GetRequiredMachineBits(void)
339 uint8_t * kw = GetMetadata("requires_machine\x09");
340 uint32_t kwLen = strlen((char *)kw);
345 for(uint32_t i=0; i<kwLen; i++)
347 type[typeLen++] = kw[i];
349 if ((kw[i + 1] == '|') || (kw[i + 1] == '\0'))
353 if (strcmp(type, "2") == 0)
355 else if (strcmp(type, "2+") == 0)
357 else if (strcmp(type, "2e") == 0)
359 else if (strcmp(type, "2c") == 0)
361 else if (strcmp(type, "2e+") == 0)
363 else if (strcmp(type, "2gs") == 0)
365 else if (strcmp(type, "2c+") == 0)
367 else if (strcmp(type, "3") == 0)
369 else if (strcmp(type, "3+") == 0)
381 uint16_t GetRequiredRAMInK(void)
383 char * kw = (char *)GetMetadata("requires_ram\x09");
385 if (strcmp(kw, "16K") == 0)
387 else if (strcmp(kw, "24K") == 0)
389 else if (strcmp(kw, "32K") == 0)
391 else if (strcmp(kw, "48K") == 0)
393 else if (strcmp(kw, "64K") == 0)
395 else if (strcmp(kw, "128K") == 0)
397 else if (strcmp(kw, "256K") == 0)
399 else if (strcmp(kw, "512K") == 0)
401 else if (strcmp(kw, "768K") == 0)
403 else if (strcmp(kw, "1M") == 0)
405 else if (strcmp(kw, "1.25M") == 0)
407 else if (strcmp(kw, "1.5M+") == 0)
413 METADATA Keywords (standard)
414 ----------------------------
415 title, subtitle, publisher, developer, copyright, version, language, requires_ram, requires_machine, notes, side, side_name, contributor, image_date
417 ram: 16K|24K|32K|48K|64K|128K|256K|512K|768K|1M|1.25M|1.5M+|Unknown
418 mch: 2|2+|2e|2c|2e+|2gs|2c+|3|3+