summaryrefslogtreecommitdiff
path: root/sid/sidplay-libs-2.1.0/libsidutils/src/SidUsage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sid/sidplay-libs-2.1.0/libsidutils/src/SidUsage.cpp')
-rw-r--r--sid/sidplay-libs-2.1.0/libsidutils/src/SidUsage.cpp377
1 files changed, 377 insertions, 0 deletions
diff --git a/sid/sidplay-libs-2.1.0/libsidutils/src/SidUsage.cpp b/sid/sidplay-libs-2.1.0/libsidutils/src/SidUsage.cpp
new file mode 100644
index 00000000..abeb085d
--- /dev/null
+++ b/sid/sidplay-libs-2.1.0/libsidutils/src/SidUsage.cpp
@@ -0,0 +1,377 @@
+/***************************************************************************
+ SidUsage.cpp - sidusage file support
+ -------------------
+ begin : Tues Nov 19 2002
+ copyright : (C) 2002 by Simon White
+ email : sidplay2@yahoo.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <sidplay/sidendian.h>
+#include "SidUsage.h"
+#include "smm0.h"
+
+static const char *txt_na = "SID Usage: N/A";
+static const char *txt_file = "SID Usage: Unable to open file";
+static const char *txt_corrupt = "SID Usage: File corrupt";
+static const char *txt_invalid = "SID Usage: Invalid IFF file";
+static const char *txt_missing = "SID Usage: Mandatory chunks missing";
+static const char *txt_supported = "SID Usage: IFF file not supported";
+static const char *txt_reading = "SID Usage: Error reading file";
+static const char *txt_writing = "SID Usage: Error writing file";
+
+static bool readSMM0 (FILE *file, const char **errorString,
+ sid_usage_t &usage,
+ const IffHeader &header);
+static bool writeSMM0 (FILE *file, const char **errorString,
+ const sid_usage_t &usage,
+ const SidTuneInfo &tuneInfo);
+static uint_least32_t readChunk (FILE *file, Chunk &chunk);
+static bool writeChunk (FILE *file, const Chunk &chunk,
+ uint_least32_t type,
+ uint_least32_t length = 0);
+static uint_least32_t skipChunk (FILE *file);
+
+
+SidUsage::SidUsage ()
+:m_status(false)
+{
+ m_errorString = txt_na;
+}
+
+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);
+}
+
+
+void SidUsage::write (const char *filename, const sid_usage_t &usage,
+ SidTuneInfo &tuneInfo)
+{
+ FILE *file;
+
+ m_status = false;
+ file = fopen (filename, "wb");
+ if (file == NULL)
+ {
+ m_errorString = txt_file;
+ return;
+ }
+
+ m_status = writeSMM0 (file, &m_errorString, usage, tuneInfo);
+ fclose (file);
+}
+
+
+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;
+}
+
+
+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;
+}
+
+
+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 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 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);
+}