+//
+// fileio.cpp: File handling (mainly disk related)
+//
+// by James Hammons
+// (C) 2019 Underground Software
+//
+
+#include "fileio.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "crc32.h"
+#include "log.h"
+
+
+uint8_t woz1Header[8] = { 'W', 'O', 'Z', '1', 0xFF, 0x0A, 0x0D, 0x0A };
+uint8_t woz2Header[8] = { 'W', 'O', 'Z', '2', 0xFF, 0x0A, 0x0D, 0x0A };
+uint8_t standardTMAP[160] = {
+ 0, 0, 0xFF, 1, 1, 1, 0xFF, 2, 2, 2, 0xFF, 3, 3, 3, 0xFF, 4, 4, 4, 0xFF,
+ 5, 5, 5, 0xFF, 6, 6, 6, 0xFF, 7, 7, 7, 0xFF, 8, 8, 8, 0xFF, 9, 9, 9, 0xFF,
+ 10, 10, 10, 0xFF, 11, 11, 11, 0xFF, 12, 12, 12, 0xFF, 13, 13, 13, 0xFF,
+ 14, 14, 14, 0xFF, 15, 15, 15, 0xFF, 16, 16, 16, 0xFF, 17, 17, 17, 0xFF,
+ 18, 18, 18, 0xFF, 19, 19, 19, 0xFF, 20, 20, 20, 0xFF, 21, 21, 21, 0xFF,
+ 22, 22, 22, 0xFF, 23, 23, 23, 0xFF, 24, 24, 24, 0xFF, 25, 25, 25, 0xFF,
+ 26, 26, 26, 0xFF, 27, 27, 27, 0xFF, 28, 28, 28, 0xFF, 29, 29, 29, 0xFF,
+ 30, 30, 30, 0xFF, 31, 31, 31, 0xFF, 32, 32, 32, 0xFF, 33, 33, 33, 0xFF,
+ 34, 34, 34, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+
+//
+// Note that size *must* be a valid pointer, otherwise this will go BOOM
+//
+uint8_t * ReadFile(const char * filename, uint32_t * size)
+{
+ FILE * fp = fopen(filename, "rb");
+
+ if (!fp)
+ return NULL;
+
+ fseek(fp, 0, SEEK_END);
+ *size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ uint8_t * buffer = (uint8_t *)malloc(*size);
+ fread(buffer, 1, *size, fp);
+ fclose(fp);
+
+ return buffer;
+}
+
+
+//
+// This initializes the WOZ type 2 headers
+//
+void InitWOZ2Headers(WOZ2 & woz)
+{
+ // Set up header (leave CRC as 0 for now)
+ memcpy(woz.magic, woz2Header, 8);
+ woz.crc32 = 0;
+
+ // INFO header
+ memcpy(woz.infoTag, "INFO", 4);
+ woz.infoSize = Uint32LE(60);
+ woz.infoVersion = 2;
+ woz.diskType = 1;
+ woz.writeProtected = 0;
+ woz.synchronized = 0;
+ woz.cleaned = 1;
+ memset(woz.creator, ' ', 32);
+ memcpy(woz.creator, "Apple2 emulator v1.0.0", 22);
+ woz.diskSides = 1;
+ woz.bootSectorFmt = 1;
+ woz.optimalBitTmg = 32;
+ woz.largestTrack = Uint16LE(13);
+
+ // TMAP header
+ memcpy(woz.tmapTag, "TMAP", 4);
+ woz.tmapSize = Uint32LE(160);
+
+ // TRKS header
+ memcpy(woz.trksTag, "TRKS", 4);
+}
+
+
+//
+// This is used mainly to initialize blank disks and upconvert non-WOZ disks
+//
+uint8_t * InitWOZ(uint32_t * pSize/*= NULL*/)
+{
+ uint32_t size = 1536 + (35 * (13 * 512));
+ uint8_t * data = (uint8_t *)malloc(size);
+ WOZ2 & woz = *((WOZ2 *)data);
+
+ // Zero out WOZ image in memory
+ memset(&woz, 0, size);
+
+ // Set up headers
+ InitWOZ2Headers(woz);
+ memcpy(woz.tmap, standardTMAP, 160);
+ woz.trksSize = Uint32LE(35 * (13 * 512));
+
+ for(int i=0; i<35; i++)
+ {
+ woz.track[i].startingBlock = Uint16LE(3 + (i * 13));
+ woz.track[i].blockCount = Uint16LE(13);
+ woz.track[i].bitCount = Uint32LE(51200);
+ }
+
+ // META header (how to handle? prolly with a separate pointer)
+
+ if (pSize)
+ *pSize = size;
+
+ return data;
+}
+
+
+uint8_t * UpconvertWOZ1ToWOZ2(uint8_t * woz1Data, uint32_t woz1Size, uint32_t * newSize)
+{
+ WOZ1 & woz1 = *((WOZ1 *)woz1Data);
+
+ // First, figure out how large the new structure will be in comparison to
+ // the old one...
+ uint32_t numTracks = woz1.trksSize / sizeof(WOZ1Track);
+ uint32_t metadataSize = woz1Size - (Uint32LE(woz1.trksSize) + 256);
+
+ // N.B.: # of blocks for each track will *always* be <= 13 for WOZ1
+ *newSize = 0x600 + (numTracks * (13 * 512)) + metadataSize;
+ uint8_t * woz2Data = (uint8_t *)malloc(*newSize);
+ memset(woz2Data, 0, *newSize);
+
+ WOZ2 & woz2 = *((WOZ2 *)woz2Data);
+ InitWOZ2Headers(woz2);
+
+ // Copy parts of INFO & TMAP chunks over
+ memcpy(&woz2.diskType, &woz1.diskType, 36);
+ memcpy(woz2.tmap, woz1.tmap, 160);
+//note: should check the CRC32 integrity 1st before attempting to recreate it here... (the CRC is written out when it's saved anyway, so no need to fuck with this right now)
+ woz2.crc32 = 0;
+ woz2.trksSize = Uint32LE(numTracks * (13 * 512));
+
+ // Finally, copy over the tracks
+ for(uint32_t i=0; i<numTracks; i++)
+ {
+ woz2.track[i].startingBlock = Uint16LE(3 + (i * 13));
+ woz2.track[i].blockCount = Uint16LE(13);
+ woz2.track[i].bitCount = woz1.track[i].bitCount;
+ memcpy(woz2Data + ((3 + (i * 13)) * 512), woz1.track[i].bits, 6646);
+ }
+
+ // Finally, copy over the metadata
+ memcpy(woz2Data + Uint32LE(woz2.trksSize) + 0x600,
+ woz1Data + Uint32LE(woz1.trksSize) + 0x100, metadataSize);
+
+ return woz2Data;
+}
+
+
+//
+// Check WOZ type on the passed in contents (file loaded elsewhere).
+// Returns type of WOZ if successful, 0 if not.
+//
+uint8_t CheckWOZType(const uint8_t * wozData, uint32_t wozSize)
+{
+ // Basic sanity checking
+ if ((wozData == NULL) || (wozSize < 8))
+ return 0;
+
+ if (memcmp(wozData, woz1Header, 8) == 0)
+ return 1;
+ else if (memcmp(wozData, woz2Header, 8) == 0)
+ return 2;
+
+ return 0;
+}
+
+
+//
+// Do basic sanity checks on the passed in contents (file loaded elsewhere).
+// Returns true if successful, false on failure.
+//
+bool CheckWOZIntegrity(const uint8_t * wozData, uint32_t wozSize)
+{
+ WOZ2 & woz = *((WOZ2 *)wozData);
+ uint32_t crc = CRC32(&wozData[12], wozSize - 12);
+ uint32_t wozCRC = Uint32LE(woz.crc32);
+
+ if ((wozCRC != 0) && (wozCRC != crc))
+ {
+ WriteLog("FILEIO: Corrupted data found in WOZ. CRC32: %08X, computed: %08X\n", wozCRC, crc);
+ return false;
+ }
+ else if (wozCRC == 0)
+ WriteLog("FILEIO: Warning--WOZ file has no CRC...\n");
+
+#if 0 // Need to fix this so it works with both 1 & 2 (works with only 1 ATM)
+ WriteLog("Track map:\n");
+ WriteLog(" 1 1 1 1 1 1 1 1\n");
+ WriteLog("0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.\n");
+ WriteLog("------------------------------------------------------------------------\n");
+
+ for(uint8_t j=0; j<2; j++)
+ {
+ for(uint8_t i=0; i<72; i++)
+ {
+ char buf[64] = "..";
+ buf[0] = buf[1] = '.';
+
+ if (woz.tmap[i] != 0xFF)
+ sprintf(buf, "%02d", woz.tmap[i]);
+
+ WriteLog("%c", buf[j]);
+ }
+
+ WriteLog("\n");
+ }
+
+ WriteLog("\n1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3\n");
+ WriteLog("8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5\n");
+ WriteLog("---------------------------------------------------------------------\n");
+
+ for(uint8_t j=0; j<2; j++)
+ {
+ for(uint8_t i=72; i<141; i++)
+ {
+ char buf[64] = "..";
+
+ if (woz.tmap[i] != 0xFF)
+ sprintf(buf, "%02d", woz.tmap[i]);
+
+ WriteLog("%c", buf[j]);
+ }
+
+ WriteLog("\n");
+ }
+
+ WriteLog("\n");
+
+ uint8_t numTracks = woz.trksSize / sizeof(WOZ1Track);
+
+ // N.B.: Need to check the track in tmap[] to have this tell the correct track... Right now, it doesn't
+ for(uint8_t i=0; i<numTracks; i++)
+ {
+ WriteLog("WOZ: Stream %u: %d bits (packed into %d bytes)\n", i, woz.track[i].bitCount, (woz.track[i].bitCount + 7) / 8);
+ }
+#endif
+
+ WriteLog("FILEIO: Well formed WOZ file found\n");
+ return true;
+}
+
+
+bool SaveWOZ(const char * filename, WOZ2 * woz, uint32_t size)
+{
+ // Set up CRC32 before writing
+ woz->crc32 = Uint32LE(CRC32(woz->infoTag, size - 12));
+
+ // META header (skip for now) (actually, should be in the disk[] image already)
+
+ // Finally, write the damn image
+ FILE * file = fopen(filename, "wb");
+
+ if (file == NULL)
+ {
+ WriteLog("FILEIO: Failed to open image file '%s' for writing...\n", filename);
+ return false;
+ }
+
+ fwrite((uint8_t *)woz, 1, size, file);
+ fclose(file);
+
+ WriteLog("FILEIO: Successfully wrote image file '%s'...\n", filename);
+
+ return true;
+}
+