void SidUsage::writeSMM (FILE *file, const sid2_usage_t &usage) { Smm_v0 smm(0); uint8_t tmp[4] = {0}; uint_least32_t length = 4; fpos_t pos; // Write out header endian_big32 (tmp, FORM_ID); if (!fwrite (&tmp, sizeof (tmp), 1, file)) goto writeSMM_error; fgetpos (file, &pos); endian_big32 (tmp, 0); if (!fwrite (&tmp, sizeof (tmp), 1, file)) goto writeSMM_error; endian_big32 (tmp, SMM0_ID); if (!fwrite (&tmp, sizeof (tmp), 1, file)) goto writeSMM_error; // Write file if (!smm.write (file, usage, length)) goto writeSMM_error; // Write final length fsetpos (file, &pos); endian_big32 (tmp, length); if (!fwrite (&tmp, sizeof (tmp), 1, file)) goto writeSMM_error; m_status = true; return; writeSMM_error: m_errorString = txt_writing; }
void SidUsage::read (const char *filename, sid_usage_t &usage) { FILE *file; uint8_t chunk[4] = {0}; IffHeader header; uint_least32_t length; m_status = false; file = fopen (filename, "wb"); if (file == NULL) { m_errorString = txt_file; goto SidTune_read_error; } // Read header chunk fread (&chunk, sizeof (chunk), 1, file); if (endian_big32 (chunk) != FORM_ID) { m_errorString = txt_invalid; goto SidTune_read_error; } length = readChunk (file, header); if (!length) { m_errorString = txt_invalid; goto SidTune_read_error; } // Determine SMM version switch (endian_big32 (header.type)) { case SMM0_ID: m_status = readSMM0 (file, &m_errorString, usage, header); break; case SMM1_ID: case SMM2_ID: case SMM3_ID: case SMM4_ID: case SMM5_ID: case SMM6_ID: case SMM7_ID: case SMM8_ID: case SMM9_ID: m_errorString = txt_invalid; goto SidTune_read_error; default: m_errorString = txt_supported; goto SidTune_read_error; } fclose (file); return; SidTune_read_error: if (file != NULL) fclose (file); }
bool SidUsage::readSMM (FILE *file, sid2_usage_t &usage, const char *) { uint8_t tmp[4] = {0}; uint_least32_t length, id; // Read header chunk fread (&tmp, sizeof (tmp), 1, file); if (endian_big32 (tmp) != FORM_ID) return false; // Read file length if (!fread (&tmp, sizeof (tmp), 1, file)) return false; length = endian_big32 (tmp); if (length < (sizeof(uint8_t) * 4)) return false; length -= (sizeof(uint8_t) * 4); // Read smm version if (!fread (&tmp, sizeof (tmp), 1, file)) return false; id = endian_big32 (tmp); // Determine SMM version switch (id) { case SMM0_ID: { Smm_v0 smm(0); m_status = smm.read (file, usage, length); if (!m_status) m_errorString = txt_corrupt; break; } case SMM1_ID: case SMM2_ID: case SMM3_ID: case SMM4_ID: case SMM5_ID: case SMM6_ID: case SMM7_ID: case SMM8_ID: case SMM9_ID: default: m_errorString = txt_supported; break; } return true; }
uint_least32_t skipChunk (FILE *file) { uint8_t tmp[4]; uint_least32_t ret; ret = fread (tmp, sizeof(tmp), 1, file); if ( ret != 1 ) return 0; return endian_big32 (tmp); }
bool writeChunk (FILE *file, const Chunk &chunk, uint_least32_t type, uint_least32_t length) { uint8_t tmp[4]; size_t ret; if (chunk.length) { endian_big32 (tmp, type); ret = fwrite (tmp, sizeof(tmp), 1, file); if ( ret != 1 ) return false; if (length == 0) length = chunk.length; endian_big32 (tmp, length); ret = fwrite (tmp, sizeof(tmp), 1, file); if ( ret != 1 ) return false; ret = fwrite ((const char *) (&chunk+1), chunk.length, 1, file); if ( ret != 1 ) return false; } return true; }
uint_least32_t readChunk (FILE *file, Chunk &chunk) { uint8_t tmp[4]; size_t ret; uint_least32_t l; ret = fread (tmp, sizeof(tmp), 1, file); if ( ret != 1 ) return 0; l = endian_big32 (tmp); if (l < chunk.length) chunk.length = l; ret = fread ((char *) (&chunk+1), chunk.length, 1, file); if ( ret != 1 ) return 0; return l; }
bool writeSMM0 (FILE *file, const char **errorString, const sid_usage_t &usage, const SidTuneInfo &tuneInfo) { struct Smm_v0 smm0; uint_least32_t headings = 3; /* Mandatory */ endian_big32 (smm0.header.type, SMM0_ID); endian_big16 (smm0.error.flags, (uint_least16_t) usage.flags); // Optional if (usage.length == 0) smm0.time.length = 0; else { endian_big16 (smm0.time.stamp, (uint_least16_t) usage.length); headings++; } { uint_least16_t load = tuneInfo.loadAddr; uint_least16_t last = load + (tuneInfo.c64dataLen - 1); endian_big16 (smm0.info.startAddr, load); endian_big16 (smm0.info.stopAddr, last); } // Optional if ( usage.md5[0] == '\0' ) smm0.md5.length = 0; else { {for (int i = 0; i < 32; i++) smm0.md5.key[i] = usage.md5[i]; } headings++; } { uint8_t i = 0; smm0.body.length = 0; {for (int page = 0; page < 0x100; page++) { char used = 0; for (int j = 0; j < 0x100; j++) used |= (usage.memory[(page << 8) | j] & 0x7f); if (used) { smm0.body.length += 0x101; memcpy (smm0.body.usage[i].flags, &usage.memory[page << 8], 0x100); smm0.body.usage[i].page = (uint8_t) page; i++; } }} } uint_least32_t filelength = smm0.header.length + smm0.error.length + smm0.info.length + smm0.md5.length + smm0.time.length + smm0.body.length + (sizeof (uint8_t) * 8 * headings); if ( writeChunk (file, smm0.header, FORM_ID, filelength) == false ) goto writeSMM0_error; if ( writeChunk (file, smm0.error, ERR0_ID) == false ) goto writeSMM0_error; if ( writeChunk (file, smm0.info, INF0_ID) == false ) goto writeSMM0_error; if ( writeChunk (file, smm0.md5, MD5_ID) == false ) goto writeSMM0_error; if ( writeChunk (file, smm0.time, TIME_ID) == false ) goto writeSMM0_error; if ( writeChunk (file, smm0.body, BODY_ID) == false ) goto writeSMM0_error; return true; writeSMM0_error: *errorString = txt_writing; return false; }
bool readSMM0 (FILE *file, const char **errorString, sid_usage_t &usage, const IffHeader &header) { Smm_v0 smm; smm.header = header; { // Read file long pos = ftell (file); bool error = true; for(;;) { size_t ret; uint_least32_t length = 0; uint8_t chunk[4]; // Read a chunk header ret = fread (&chunk, sizeof (chunk), 1, file); // If no chunk header assume end of file if (ret != 1) break; // Check for a chunk we are interested in switch (endian_big32 (chunk)) { case INF0_ID: length = readChunk (file, smm.info); break; case ERR0_ID: length = readChunk (file, smm.error); break; case MD5_ID: length = readChunk (file, smm.md5); break; case TIME_ID: length = readChunk (file, smm.time); break; case BODY_ID: length = readChunk (file, smm.body); break; default: length = skipChunk (file); } if (!length) { error = true; break; } // Move past the chunk pos += (long) length + (sizeof(uint8_t) * 8); fseek (file, pos, SEEK_SET); if (ftell (file) != pos) { error = true; break; } error = false; } // Check for file reader error if (error) { *errorString = txt_reading; return false; } } // Test that all required checks were found if ((smm.info.length == 0) || (smm.error.length == 0) || (smm.body.length == 0)) { *errorString = txt_missing; return false; } // Extract usage information {for (int i = 0; i < 0x100; i++) { int addr = smm.body.usage[i].page << 8; if ((addr == 0) && (i != 0)) break; memcpy (&usage.memory[addr], smm.body.usage[i].flags, sizeof (uint8_t) * 0x100); }} { // File in the load range uint_least16_t load, last; int length; load = endian_big16 (smm.info.startAddr); last = endian_big16 (smm.info.stopAddr); length = (int) (last - load) + 1; if (length < 0) { *errorString = txt_corrupt; return false; } {for (int i = 0; i < length; i++) usage.memory[load + i] |= SID_LOAD_IMAGE; } } usage.flags = endian_big16(smm.error.flags); return true; }