0
|
1 #include "th_args.h"
|
|
2 #include "th_endian.h"
|
|
3 #include "th_crypto.h"
|
|
4
|
1
|
5 #define PSID_BUFFER_SIZE (1024 * 16)
|
|
6
|
0
|
7
|
|
8 typedef struct
|
|
9 {
|
|
10 char magic[4]; // "PSID" / "RSID" magic identifier
|
|
11 uint16_t version, // Version number
|
|
12 dataOffset, // Start of actual c64 data in file
|
|
13 loadAddress, // Loading address
|
|
14 initAddress, // Initialization address
|
|
15 playAddress, // Play one frame
|
|
16 nSongs, // Number of subsongs
|
|
17 startSong; // Default starting song
|
|
18 uint32_t speed; // Speed
|
|
19 char sidName[32]; // Descriptive text-fields, ASCIIZ
|
|
20 char sidAuthor[32];
|
|
21 char sidCopyright[32];
|
|
22
|
|
23 // PSIDv2 data
|
|
24 uint16_t flags; // Flags
|
|
25 uint8_t startPage, pageLength;
|
|
26 uint16_t reserved;
|
|
27
|
1
|
28 // Extra data
|
0
|
29 BOOL isRSID;
|
1
|
30 size_t dataSize; // Total size of data - header
|
|
31 th_md5hash_t hash; // Songlength database hash
|
0
|
32
|
|
33 } PSIDHeader;
|
|
34
|
|
35
|
1
|
36 int si_read_sid_file(FILE *inFile, PSIDHeader *psid)
|
0
|
37 {
|
1
|
38 th_md5state_t state;
|
|
39 uint8_t tmp8, *fileData = NULL;
|
|
40 int index, ret = -1;
|
|
41 size_t read;
|
|
42 BOOL first;
|
|
43
|
|
44 memset(psid, 0, sizeof(*psid));
|
|
45
|
|
46 if ((fileData = (uint8_t *) th_malloc(PSID_BUFFER_SIZE)) == NULL)
|
|
47 {
|
|
48 THERR("Error allocating temporary data buffer of %d bytes.\n", PSID_BUFFER_SIZE);
|
|
49 goto error;
|
|
50 }
|
0
|
51
|
|
52 // Read PSID header in
|
1
|
53 if (!th_fread_str(inFile, (uint8_t *) psid->magic, sizeof(psid->magic)) ||
|
|
54 !th_fread_be16(inFile, &psid->version) ||
|
|
55 !th_fread_be16(inFile, &psid->dataOffset) ||
|
|
56 !th_fread_be16(inFile, &psid->loadAddress) ||
|
|
57 !th_fread_be16(inFile, &psid->initAddress) ||
|
|
58 !th_fread_be16(inFile, &psid->playAddress) ||
|
|
59 !th_fread_be16(inFile, &psid->nSongs) ||
|
|
60 !th_fread_be16(inFile, &psid->startSong) ||
|
|
61 !th_fread_be32(inFile, &psid->speed))
|
0
|
62 {
|
1
|
63 THERR("Could not read PSID/RSID header.\n");
|
0
|
64 goto error;
|
|
65 }
|
|
66
|
|
67 if ((psid->magic[0] != 'R' && psid->magic[0] != 'P') ||
|
|
68 psid->magic[1] != 'S' || psid->magic[2] != 'I' || psid->magic[3] != 'D' ||
|
|
69 psid->version < 1 || psid->version > 3)
|
|
70 {
|
1
|
71 THERR("Not a supported PSID or RSID file.\n");
|
0
|
72 goto error;
|
|
73 }
|
|
74
|
1
|
75 psid->isRSID = psid->magic[0] == 'R';
|
0
|
76
|
1
|
77 if (!th_fread_str(inFile, (uint8_t *)psid->sidName, sizeof(psid->sidName)) ||
|
|
78 !th_fread_str(inFile, (uint8_t *)psid->sidAuthor, sizeof(psid->sidAuthor)) ||
|
|
79 !th_fread_str(inFile, (uint8_t *)psid->sidCopyright, sizeof(psid->sidCopyright)))
|
0
|
80 {
|
1
|
81 THERR("Error reading SID file header.\n");
|
0
|
82 goto error;
|
|
83 }
|
|
84
|
|
85 // Check if we need to load PSIDv2NG header ...
|
|
86 if (psid->version >= 2)
|
|
87 {
|
|
88 // Yes, we need to
|
1
|
89 if (!th_fread_be16(inFile, &psid->flags) ||
|
|
90 !th_fread_byte(inFile, &psid->startPage) ||
|
|
91 !th_fread_byte(inFile, &psid->pageLength) ||
|
|
92 !th_fread_be16(inFile, &psid->reserved))
|
0
|
93 {
|
1
|
94 THERR("Error reading PSID/RSID v2+ extra header data.\n");
|
0
|
95 goto error;
|
|
96 }
|
|
97 }
|
|
98
|
1
|
99 // Initialize MD5-hash calculation
|
|
100 th_md5_init(&state);
|
0
|
101
|
1
|
102 // Process actual data
|
|
103 psid->dataSize = 0;
|
|
104 first = TRUE;
|
|
105 do {
|
|
106 read = fread(fileData, sizeof(uint8_t), PSID_BUFFER_SIZE, inFile);
|
|
107 psid->dataSize += read;
|
|
108 if (read < 16)
|
|
109 {
|
|
110 THERR("Error reading song data, unexpectedly small file.\n");
|
|
111 goto error;
|
|
112 }
|
0
|
113
|
1
|
114 if (first && psid->loadAddress == 0)
|
|
115 {
|
|
116 // Strip load address (2 first bytes)
|
|
117 th_md5_append(&state, &fileData[2], read - 2);
|
|
118 first = FALSE;
|
|
119 }
|
|
120 else
|
|
121 {
|
|
122 // Append "as is"
|
|
123 th_md5_append(&state, fileData, read);
|
|
124 }
|
|
125 } while (read > 0 && !feof(inFile));
|
0
|
126
|
1
|
127 // Append header data to hash
|
0
|
128 #define THADDHASH(QDATAB) do { \
|
|
129 uint8_t ib8[2]; \
|
|
130 ib8[0] = (QDATAB & 0xff); \
|
|
131 ib8[1] = (QDATAB >> 8); \
|
1
|
132 th_md5_append(&state, (uint8_t *) &ib8, sizeof(ib8)); \
|
0
|
133 } while (0)
|
|
134
|
|
135 THADDHASH(psid->initAddress);
|
|
136 THADDHASH(psid->playAddress);
|
|
137 THADDHASH(psid->nSongs);
|
|
138 #undef THADDHASH
|
|
139
|
|
140 // Append song speed data to hash
|
1
|
141 tmp8 = psid->isRSID ? 60 : 0;
|
0
|
142 for (index = 0; index < psid->nSongs && index < 32; index++)
|
|
143 {
|
1
|
144 if (psid->isRSID)
|
|
145 tmp8 = 60;
|
0
|
146 else
|
1
|
147 tmp8 = (psid->speed & (1 << index)) ? 60 : 0;
|
0
|
148
|
1
|
149 th_md5_append(&state, &tmp8, sizeof(tmp8));
|
0
|
150 }
|
|
151
|
|
152 // Rest of songs (more than 32)
|
|
153 for (index = 32; index < psid->nSongs; index++)
|
1
|
154 th_md5_append(&state, &tmp8, sizeof(tmp8));
|
0
|
155
|
|
156 // PSIDv2NG specific
|
|
157 if (psid->version >= 2)
|
|
158 {
|
|
159 // REFER TO SIDPLAY HEADERS FOR MORE INFORMATION
|
1
|
160 tmp8 = (psid->flags >> 2) & 3;
|
|
161 if (tmp8 == 2)
|
|
162 th_md5_append(&state, &tmp8, sizeof(tmp8));
|
0
|
163 }
|
|
164
|
|
165 // Calculate the hash
|
1
|
166 th_md5_finish(&state, psid->hash);
|
|
167 ret = 0;
|
0
|
168
|
1
|
169 error:
|
0
|
170 // Free buffer
|
1
|
171 th_free(fileData);
|
|
172 return ret;
|
0
|
173 }
|
|
174
|
|
175
|
|
176
|
|
177 int main(int argc, char *argv[])
|
|
178 {
|
|
179 // Initialize
|
|
180 th_init("SIDInfo", "PSID/RSID information displayer", "0.1", NULL, NULL);
|
|
181 th_verbosityLevel = 0;
|
|
182
|
|
183 // Try to open the file
|
1
|
184 // if ((inFile = fopen(inFilename, "rb")) == NULL)
|
|
185 // goto error;
|
0
|
186
|
1
|
187
|
|
188 return 0;
|
0
|
189 }
|