1 /*
2 * /home/ms/files/source/libsidtune/RCS/IconInfo.cpp,v
3 *
4 * Amiga icon tooltype PlaySID file format (.info) support.
5 *
6 * This is a derived work, courtesy of Peter Kunath, who has
7 * provided an examplary source code to examine an Amiga icon file.
8 *
9 * It has been ported and heavily modified to suit certain
10 * requirements. This replaces the old code, which was simply
11 * scanning input data for a first, presumedly constant, Id string.
12 * This code does not require the default tool to serve as a
13 * constant Id by containing "SID:PlaySID".
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 */
29
30 #include "config.h"
31 #include "SidTune.h"
32 #include "SmartPtr.h"
33 #include "SidTuneTools.h"
34 #include "sidendian.h"
35
36 #ifdef HAVE_EXCEPTIONS
37 # include <new>
38 #endif
39 #include <string.h>
40
41 // Amiga Workbench specific structures.
42
43 struct Border
44 {
45 uint8_t LeftEdge[2]; // uint_least16_t; initial offsets from the origin
46 uint8_t TopEdge[2]; // uint_least16_t
47 uint8_t FrontPen, BackPen; // pens numbers for rendering
48 uint8_t DrawMode; // mode for rendering
49 uint8_t Count; // number of XY pairs
50 uint8_t pXY[4]; // int_least16_t *XY; vector coordinate pairs rel to LeftTop
51 uint8_t pNextBorder[4]; // Border *NextBorder; pointer to any other Border too
52 };
53
54 struct Image
55 {
56 uint8_t LeftEdge[2]; // uint_least16_t; starting offset relative to some origin
57 uint8_t TopEdge[2]; // uint_least16_t; starting offsets relative to some origin
58 uint8_t Width[2]; // uint_least16_t; pixel size (though data is word-aligned)
59 uint8_t Height[2]; // uint_least16_t
60 uint8_t Depth[2]; // uint_least16_t; >= 0, for images you create
61 uint8_t pImageData[4]; // uint_least16_t *ImageData; pointer to the actual word-aligned bits
62 uint8_t PlanePick, PlaneOnOff;
63 uint8_t pNextImage[4]; // Image *NextImage;
64 };
65
66 struct Gadget
67 {
68 uint8_t pNextGadget[4]; // Gadget *NextGadget; next gadget in the list
69 uint8_t LeftEdge[2]; // uint_least16_t; "hit box" of gadget
70 uint8_t TopEdge[2]; // uint_least16_t
71 uint8_t Width[2]; // uint_least16_t; "hit box" of gadget
72 uint8_t Height[2]; // uint_least16_t
73 uint8_t Flags[2]; // uint_least16_t; see below for list of defines
74 uint8_t Activation[2]; // uint_least16_t
75 uint8_t GadgetType[2]; // uint_least16_t; see below for defines
76 uint8_t pGadgetRender[4]; // Image *GadgetRender;
77 uint8_t pSelectRender[4]; // Image *SelectRender;
78 uint8_t pGadgetText[4]; // void *GadgetText;
79 uint8_t MutualExclude[4]; // udword
80 uint8_t pSpecialInfo[4]; // void *SpecialInfo;
81 uint8_t GadgetID[2]; // uint_least16_t
82 uint8_t UserData[4]; // udword; ptr to general purpose User data
83 };
84
85 struct DiskObject
86 {
87 uint8_t Magic[2]; // uint_least16_t; a magic num at the start of the file
88 uint8_t Version[2]; // uint_least16_t; a version number, so we can change it
89 struct Gadget Gadget; // a copy of in core gadget
90 uint8_t Type;
91 uint8_t PAD_BYTE; // Pad it out to the next word boundry
92 uint8_t pDefaultTool[4]; // char *DefaultTool;
93 uint8_t ppToolTypes[4]; // char **ToolTypes;
94 uint8_t CurrentX[4]; // udword
95 uint8_t CurrentY[4]; // udword
96 uint8_t pDrawerData[4]; // char *DrawerData;
97 uint8_t pToolWindow[4]; // char *ToolWindow; only applies to tools
98 uint8_t StackSize[4]; // udword; only applies to tools
99 };
100
101
102 // A magic number, not easily impersonated.
103 #define WB_DISKMAGIC 0xE310
104 // Our current version number.
105 #define WB_DISKVERSION 1
106 // Our current revision number.
107 #define WB_DISKREVISION 1
108 // I only use the lower 8 bits of Gadget.UserData for the revision #.
109 #define WB_DISKREVISIONMASK 0xFF
110
111 // The Workbench object types.
112 #define WB_DISK 1
113 #define WB_DRAWER 2
114 #define WB_TOOL 3
115 #define WB_PROJECT 4
116 #define WB_GARBAGE 5
117 #define WB_DEVICE 6
118 #define WB_KICK 7
119 #define WB_APPICON 8
120
121 // --- Gadget.Flags values ---
122 // Combinations in these bits describe the highlight technique to be used.
123 #define GFLG_GADGHIGHBITS 0x0003
124 // Complement the select box.
125 #define GFLG_GADGHCOMP 0x0000
126 // Draw a box around the image.
127 #define GFLG_GADGHBOX 0x0001
128 // Blast in this alternate image.
129 #define GFLG_GADGHIMAGE 0x0002
130 // Don't highlight.
131 #define GFLG_GADGHNONE 0x0003
132 // Set if GadgetRender and SelectRender point to an Image structure,
133 // clear if they point to Border structures.
134 #define GFLG_GADGIMAGE 0x0004
135
136 const char _sidtune_txt_format[] = "Raw plus PlaySID icon tooltype file (INFO)";
137
138 const char _sidtune_keyword_id[] = "SID:PLAYSID";
139 const char _sidtune_keyword_address[] = "ADDRESS=";
140 const char _sidtune_keyword_songs[] = "SONGS=";
141 const char _sidtune_keyword_speed[] = "SPEED=";
142 const char _sidtune_keyword_name[] = "NAME=";
143 const char _sidtune_keyword_author[] = "AUTHOR=";
144 const char _sidtune_keyword_copyright[] = "COPYRIGHT=";
145 const char _sidtune_keyword_musPlayer[] = "SIDSONG=YES";
146
147 const char _sidtune_txt_noMemError[] = "ERROR: Not enough free memory";
148 const char _sidtune_txt_corruptError[] = "ERROR: Info file is incomplete or corrupt";
149 const char _sidtune_txt_noStringsError[] = "ERROR: Info file does not contain required strings";
150 const char _sidtune_txt_dataCorruptError[] = "ERROR: C64 data file is corrupt";
151 #if defined(SIDTUNE_REJECT_UNKNOWN_FIELDS)
152 const char _sidtune_txt_chunkError[] = "ERROR: Invalid tooltype information in icon file";
153 #endif
154
155 const uint_least16_t safeBufferSize = 64; // for string comparison, stream parsing
156
157
INFO_fileSupport(const void * dataBuffer,uint_least32_t dataLength,const void * infoBuffer,uint_least32_t infoLength)158 bool SidTune::INFO_fileSupport(const void* dataBuffer, uint_least32_t dataLength,
159 const void* infoBuffer, uint_least32_t infoLength)
160 {
161 // Require a first minimum safety size.
162 uint_least32_t minSize = 1+sizeof(struct DiskObject);
163 if (infoLength < minSize)
164 return( false );
165
166 const DiskObject *dobject = (const DiskObject *)infoBuffer;
167
168 // Require Magic_Id in the first two bytes of the file.
169 if ( endian_16(dobject->Magic[0],dobject->Magic[1]) != WB_DISKMAGIC )
170 return false;
171
172 // Only version 1.x supported.
173 if ( endian_16(dobject->Version[0],dobject->Version[1]) != WB_DISKVERSION )
174 return false;
175
176 // A PlaySID icon must be of type project.
177 if ( dobject->Type != WB_PROJECT )
178 return false;
179
180 uint i; // general purpose index variable
181
182 // We want to skip a possible Gadget Image item.
183 const char *icon = (const char*)infoBuffer + sizeof(DiskObject);
184
185 if ( (endian_16(dobject->Gadget.Flags[0],dobject->Gadget.Flags[1]) & GFLG_GADGIMAGE) == 0)
186 {
187 // Calculate size of gadget borders (vector image).
188
189 if (dobject->Gadget.pGadgetRender[0] |
190 dobject->Gadget.pGadgetRender[1] |
191 dobject->Gadget.pGadgetRender[2] |
192 dobject->Gadget.pGadgetRender[3]) // border present?
193 {
194 // Require another minimum safety size.
195 minSize += sizeof(struct Border);
196 if (infoLength < minSize)
197 return( false );
198
199 const Border *brd = (const Border *)icon;
200 icon += sizeof(Border);
201 icon += brd->Count * (2+2); // pair of uint_least16_t
202 }
203
204 if (dobject->Gadget.pSelectRender[0] |
205 dobject->Gadget.pSelectRender[1] |
206 dobject->Gadget.pSelectRender[2] |
207 dobject->Gadget.pSelectRender[3]) // alternate border present?
208 {
209 // Require another minimum safety size.
210 minSize += sizeof(Border);
211 if (infoLength < minSize)
212 return( false );
213
214 const Border *brd = (const Border *)icon;
215 icon += sizeof(Border);
216 icon += brd->Count * (2+2); // pair of uint_least16_t
217 }
218 }
219 else
220 {
221 // Calculate size of gadget images (bitmap image).
222
223 if (dobject->Gadget.pGadgetRender[0] |
224 dobject->Gadget.pGadgetRender[1] |
225 dobject->Gadget.pGadgetRender[2] |
226 dobject->Gadget.pGadgetRender[3]) // image present?
227 {
228 // Require another minimum safety size.
229 minSize += sizeof(Image);
230 if (infoLength < minSize)
231 return( false );
232
233 const Image *img = (const Image *)icon;
234 icon += sizeof(Image);
235
236 uint_least32_t imgsize = 0;
237 for(i=0;i<endian_16(img->Depth[0],img->Depth[1]);i++)
238 {
239 if ( (img->PlanePick & (1<<i)) != 0)
240 {
241 // NOTE: Intuition relies on PlanePick to know how many planes
242 // of data are found in ImageData. There should be no more
243 // '1'-bits in PlanePick than there are planes in ImageData.
244 imgsize++;
245 }
246 }
247
248 imgsize *= ((endian_16(img->Width[0],img->Width[1])+15)/16)*2; // bytes per line
249 imgsize *= endian_16(img->Height[0],img->Height[1]); // bytes per plane
250
251 icon += imgsize;
252 }
253
254 if (dobject->Gadget.pSelectRender[0] |
255 dobject->Gadget.pSelectRender[1] |
256 dobject->Gadget.pSelectRender[2] |
257 dobject->Gadget.pSelectRender[3]) // alternate image present?
258 {
259 // Require another minimum safety size.
260 minSize += sizeof(Image);
261 if (infoLength < minSize)
262 return( false );
263
264 const Image *img = (const Image *)icon;
265 icon += sizeof(Image);
266
267 uint_least32_t imgsize = 0;
268 for(i=0;i<endian_16(img->Depth[0],img->Depth[1]);i++)
269 {
270 if ( (img->PlanePick & (1<<i)) != 0)
271 {
272 // NOTE: Intuition relies on PlanePick to know how many planes
273 // of data are found in ImageData. There should be no more
274 // '1'-bits in PlanePick than there are planes in ImageData.
275 imgsize++;
276 }
277 }
278
279 imgsize *= ((endian_16(img->Width[0],img->Width[1])+15)/16)*2; // bytes per line
280 imgsize *= endian_16(img->Height[0],img->Height[1]); // bytes per plane
281 icon += imgsize;
282 }
283 }
284
285 // Here use a smart pointer to prevent access violation errors.
286 SmartPtr_sidtt<const char> spTool((const char*)icon,infoLength-(uint_least32_t)(icon-(const char*)infoBuffer));
287 if ( !spTool )
288 {
289 info.formatString = _sidtune_txt_corruptError;
290 return false;
291 }
292
293 // A separate safe buffer is used for each tooltype string.
294 #ifdef HAVE_EXCEPTIONS
295 SmartPtr_sidtt<char> spCmpBuf(new(std::nothrow) char[safeBufferSize],safeBufferSize,true);
296 #else
297 SmartPtr_sidtt<char> spCmpBuf(new char[safeBufferSize],safeBufferSize,true);
298 #endif
299 if ( !spCmpBuf )
300 {
301 info.formatString = _sidtune_txt_noMemError;
302 return false;
303 }
304
305 #ifndef SID_HAVE_BAD_COMPILER
306 char* cmpBuf = spCmpBuf.tellBegin();
307 #else
308 // This should not be necessary, but for some reason
309 // Microsoft Visual C++ says spCmpBuf is const...
310 char* cmpBuf = (char*) spCmpBuf.tellBegin();
311 #endif
312
313 // Skip default tool.
314 spTool += endian_32(spTool[0],spTool[1],spTool[2],spTool[3]) + 4;
315
316 // Defaults.
317 fileOffset = 0; // no header in separate data file
318 info.sidChipBase1 = 0xd400;
319 info.sidChipBase2 = 0;
320 info.musPlayer = false;
321 info.numberOfInfoStrings = 0;
322 uint_least32_t oldStyleSpeed = 0;
323
324 // Flags for required entries.
325 bool hasAddress = false,
326 hasName = false,
327 hasAuthor = false,
328 hasCopyright = false,
329 hasSongs = false,
330 hasSpeed = false,
331 hasUnknownChunk = false;
332
333 // Calculate number of tooltype strings.
334 i = (endian_32(spTool[0],spTool[1],spTool[2],spTool[3])/4) - 1;
335 spTool += 4; // skip size info
336
337 while( i-- > 0 )
338 {
339 // Get length of this tool.
340 uint_least32_t toolLen = endian_32(spTool[0],spTool[1],spTool[2],spTool[3]);
341 spTool += 4; // skip tool length
342 // Copy item to safe buffer.
343 for ( uint ci = 0; ci < toolLen; ci++ )
344 {
345 #ifndef SID_HAVE_BAD_COMPILER
346 spCmpBuf[ci] = spTool[ci];
347 #else
348 // This should not be necessary, but for some reason
349 // Microsoft Visual C++ says spCmpBuf is const...
350 (*((char*) (&spCmpBuf[ci]))) = (char) spTool[ci];
351 #endif
352 }
353 if ( !(spTool&&spCmpBuf) )
354 {
355 return false;
356 }
357
358 // Now check all possible keywords.
359 if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_address) == 0 )
360 {
361 const char *addrIn= cmpBuf + strlen(_sidtune_keyword_address);
362 int len = toolLen - strlen(_sidtune_keyword_address);
363 int pos = 0;
364 info.loadAddr = (uint_least16_t)SidTuneTools::readHex( addrIn, len, pos );
365 info.initAddr = (uint_least16_t)SidTuneTools::readHex( addrIn, len, pos );
366 info.playAddr = (uint_least16_t)SidTuneTools::readHex( addrIn, len, pos );
367 if ( pos >= len )
368 {
369 return false;
370 }
371 hasAddress = true;
372 }
373 else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_songs) == 0 )
374 {
375 const char *numIn = cmpBuf + strlen(_sidtune_keyword_songs);
376 int len = toolLen - strlen(_sidtune_keyword_songs);
377 int pos = 0;
378 if ( !pos >= len )
379 {
380 return false;
381 }
382 info.songs = (uint_least16_t)SidTuneTools::readDec( numIn, len, pos );
383 info.startSong = (uint_least16_t)SidTuneTools::readDec( numIn, len, pos );
384 hasSongs = true;
385 }
386 else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_speed) == 0 )
387 {
388 const char *speedIn = cmpBuf + strlen(_sidtune_keyword_speed);
389 int len = toolLen - strlen(_sidtune_keyword_speed);
390 int pos = 0;
391 if ( pos >= len )
392 {
393 return false;
394 }
395 oldStyleSpeed = SidTuneTools::readHex(speedIn, len, pos);
396 hasSpeed = true;
397 }
398 else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_name) == 0 )
399 {
400 strncpy( &infoString[0][0], cmpBuf + strlen(_sidtune_keyword_name), 31 );
401 info.infoString[0] = &infoString[0][0];
402 hasName = true;
403 }
404 else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_author) == 0 )
405 {
406 strncpy( &infoString[1][0], cmpBuf + strlen(_sidtune_keyword_author), 31 );
407 info.infoString[1] = &infoString[1][0];
408 hasAuthor = true;
409 }
410 else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_copyright) == 0 )
411 {
412 strncpy( &infoString[2][0], cmpBuf + strlen(_sidtune_keyword_copyright), 31 );
413 info.infoString[2] = &infoString[2][0];
414 hasCopyright = true;
415 }
416 else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_musPlayer) == 0 )
417 {
418 info.musPlayer = true;
419 }
420 else
421 {
422 hasUnknownChunk = true;
423 #if defined(SIDTUNE_REJECT_UNKNOWN_FIELDS)
424 info.formatString = _sidtune_txt_chunkError;
425 return false;
426 #endif
427 }
428 // Skip to next tool.
429 spTool += toolLen;
430 }
431
432 // Collected ``required'' information complete ?
433 if ( hasAddress && hasName && hasAuthor && hasCopyright && hasSongs && hasSpeed )
434 {
435 // Create the speed/clock setting table.
436 convertOldStyleSpeedToTables(oldStyleSpeed);
437 if (( info.loadAddr == 0 ) && ( dataLength != 0 ))
438 {
439 SmartPtr_sidtt<const uint_least8_t> spDataBuf((const uint_least8_t*)dataBuffer,dataLength);
440 spDataBuf += fileOffset;
441 info.loadAddr = endian_16(spDataBuf[1],spDataBuf[0]);
442 if ( !spDataBuf )
443 {
444 info.formatString = _sidtune_txt_dataCorruptError;
445 return false;
446 }
447 fileOffset += 2;
448 }
449 if ( info.initAddr == 0 )
450 {
451 info.initAddr = info.loadAddr;
452 }
453 info.numberOfInfoStrings = 3;
454 // We finally accept the input data.
455 info.formatString = _sidtune_txt_format;
456 return true;
457 }
458 else if ( hasAddress || hasName || hasAuthor || hasCopyright || hasSongs || hasSpeed )
459 {
460 // Something is missing (or damaged?).
461 info.formatString = _sidtune_txt_corruptError;
462 return false;
463 }
464 else
465 {
466 // No PlaySID conform info strings.
467 info.formatString = _sidtune_txt_noStringsError;
468 return false;
469 }
470 }
471