File: info_.cpp

package info (click to toggle)
sidplay 1.36.28-2
  • links: PTS
  • area: main
  • in suites: slink
  • size: 1,192 kB
  • ctags: 1,674
  • sloc: cpp: 12,514; sh: 1,716; makefile: 223
file content (342 lines) | stat: -rw-r--r-- 11,902 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
//
// /home/ms/source/sidplay/libsidplay/fformat/RCS/info_.cpp,v
//
// Amiga PlaySID icon tooltype file format (.INFO) support.
//
// This is a derived work, courtesy of Peter Kunath, who has provided an
// examplary source code to examine an Amiga icon file.
//
// It has been ported and heavily modified to suit certain requirements.
//
// This replaces the old code, which was simply scanning input data for
// a first, presumedly constant, Id string. This code does not require the
// default tool to serve as a constant Id by containing "SID:PlaySID".

#include "info_.h"


const char text_format[] = "Raw plus PlaySID icon tooltype file (INFO)";

const char keyword_id[] = "SID:PLAYSID";
const char keyword_address[] = "ADDRESS=";
const char keyword_songs[] = "SONGS=";
const char keyword_speed[] = "SPEED=";
const char keyword_name[] = "NAME=";
const char keyword_author[] = "AUTHOR=";
const char keyword_copyright[] = "COPYRIGHT=";
const char keyword_musPlayer[] = "SIDSONG=YES";

const char text_noMemError[] = "ERROR: Not enough free memory";
const char text_corruptError[] = "ERROR: Info file is incomplete or corrupt";
const char text_noStringsError[] = "ERROR: Info file does not contain required strings";
const char text_dataCorruptError[] = "ERROR: C64 data file is corrupt";
#if defined(SIDTUNE_REJECT_UNKNOWN_FIELDS)
const char text_chunkError[] = "ERROR: Invalid tooltype information in icon file";
#endif

const uint safeBufferSize = 64;  // for string comparison, stream parsing


bool copyItem(smartPtr<const char>& spIn, smartPtr<char>& spCmpBuf, udword itemLen)
{
    for ( uword i = 0; i < itemLen; i++ )
    {
        spCmpBuf[i] = spIn[i];
    }
    return (spIn && spCmpBuf);
}


bool sidTune::INFO_fileSupport(const void* dataBuffer, udword dataLength,
                               const void* infoBuffer, udword infoLength)
{
    // Remove any format description or format error string.
    info.formatString = 0;

    // Require a first minimum safety size.
    ulong minSize = 1+sizeof(struct DiskObject);
    if (infoLength < minSize)
        return( false );

    const DiskObject *dobject = (const DiskObject *)infoBuffer;

    // Require Magic_Id in the first two bytes of the file.
    if ( readEndian(dobject->Magic[0],dobject->Magic[1]) != WB_DISKMAGIC )
        return false;

    // Only version 1.x supported.
    if ( readEndian(dobject->Version[0],dobject->Version[1]) != WB_DISKVERSION )
        return false;

    // A PlaySID icon must be of type project.
    if ( dobject->Type != WB_PROJECT )
        return false;

    int i;  // general purpose index variable

    // We want to skip a possible Gadget Image item.
    const char *icon = (const char*)infoBuffer + sizeof(DiskObject);

    if ( (readEndian(dobject->Gadget.Flags[0],dobject->Gadget.Flags[1]) & GFLG_GADGIMAGE) == 0)
    {
        // Calculate size of gadget borders (vector image).
		
        if (dobject->Gadget.pGadgetRender[0] |
            dobject->Gadget.pGadgetRender[1] |
            dobject->Gadget.pGadgetRender[2] |
            dobject->Gadget.pGadgetRender[3])  // border present?
        {
            // Require another minimum safety size.
            minSize += sizeof(struct Border);
            if (infoLength < minSize)
                return( false );

            const Border *brd = (const Border *)icon;
            icon += sizeof(Border);
            icon += brd->Count * (2+2);	       // pair of uword
        }

        if (dobject->Gadget.pSelectRender[0] |
            dobject->Gadget.pSelectRender[1] |
            dobject->Gadget.pSelectRender[2] |
            dobject->Gadget.pSelectRender[3])  // alternate border present?
        {
            // Require another minimum safety size.
            minSize += sizeof(Border);
            if (infoLength < minSize)
                return( false );

            const Border *brd = (const Border *)icon;
            icon += sizeof(Border);
            icon += brd->Count * (2+2);	       // pair of uword
        }
    }
    else
    {
        // Calculate size of gadget images (bitmap image).

        if (dobject->Gadget.pGadgetRender[0] |
            dobject->Gadget.pGadgetRender[1] |
            dobject->Gadget.pGadgetRender[2] |
            dobject->Gadget.pGadgetRender[3])  // image present?
        {
            // Require another minimum safety size.
            minSize += sizeof(Image);
            if (infoLength < minSize)
                return( false );

            const Image *img = (const Image *)icon;
            icon += sizeof(Image);

            udword imgsize = 0;
            for(i=0;i<readEndian(img->Depth[0],img->Depth[1]);i++)
            {
                if ( (img->PlanePick & (1<<i)) != 0)
                {
                    // NOTE: Intuition relies on PlanePick to know how many planes
                    // of data are found in ImageData. There should be no more
                    // '1'-bits in PlanePick than there are planes in ImageData.
                    imgsize++;
                }
            }

            imgsize *= ((readEndian(img->Width[0],img->Width[1])+15)/16)*2;  // bytes per line
            imgsize *= readEndian(img->Height[0],img->Height[1]);            // bytes per plane

            icon += imgsize;
        }
      
        if (dobject->Gadget.pSelectRender[0] |
            dobject->Gadget.pSelectRender[1] |
            dobject->Gadget.pSelectRender[2] |
            dobject->Gadget.pSelectRender[3])  // alternate image present?
        {
            // Require another minimum safety size.
            minSize += sizeof(Image);
            if (infoLength < minSize)
                return( false );

            const Image *img = (const Image *)icon;
            icon += sizeof(Image);

            udword imgsize = 0;
            for(i=0;i<readEndian(img->Depth[0],img->Depth[1]);i++)
            {
                if ( (img->PlanePick & (1<<i)) != 0)
                {
                    // NOTE: Intuition relies on PlanePick to know how many planes
                    // of data are found in ImageData. There should be no more
                    // '1'-bits in PlanePick than there are planes in ImageData.
                    imgsize++;
                }
            }

            imgsize *= ((readEndian(img->Width[0],img->Width[1])+15)/16)*2;  // bytes per line
            imgsize *= readEndian(img->Height[0],img->Height[1]);            // bytes per plane
            icon += imgsize;
        }
    }

    // Here use a smart pointer to prevent access violation errors.
    smartPtr<const char> spTool((const char*)icon,infoLength-(ulong)(icon-(const char*)infoBuffer));
    if ( !spTool )
    {
        info.formatString = text_corruptError;
        return false;
    }

    // A separate safe buffer is used for each tooltype string.
    smartPtr<char> spCmpBuf(new char[safeBufferSize],safeBufferSize,true);
    if ( !spCmpBuf )
    {
        info.formatString = text_noMemError;
        return false;
    }
    char* cmpBuf = spCmpBuf.tellBegin();

    // Skip default tool.
    spTool += readEndian(spTool[0],spTool[1],spTool[2],spTool[3]) + 4;

    // Defaults.
    fileOffset = 0;	               // no header in separate data file
    info.musPlayer = false;
    info.numberOfInfoStrings = 0;
    udword oldStyleSpeed = 0;

    // Flags for required entries.
    bool hasAddress = false,
        hasName = false,
        hasAuthor = false,
        hasCopyright = false,
        hasSongs = false,
        hasSpeed = false,
        hasUnknownChunk = false;

    // Calculate number of tooltype strings.
    i = (readEndian(spTool[0],spTool[1],spTool[2],spTool[3])/4) - 1;
    spTool += 4;  // skip size info

    while( i-- > 0 )
    {
        // Get length of this tool.
        udword toolLen = readEndian(spTool[0],spTool[1],spTool[2],spTool[3]);
        spTool += 4;  // skip tool length
        // Copy item to safe buffer.
        if ( !copyItem(spTool,spCmpBuf,toolLen) )
        {
            return false;
        }

        // Now check all possible keywords.
        if ( myStrNcaseCmp(cmpBuf,keyword_address) == 0 )
        {
            istrstream addrIn(cmpBuf + strlen(keyword_address),
                              toolLen - strlen(keyword_address));
            info.loadAddr = (uword)readHex( addrIn );
            info.initAddr = (uword)readHex( addrIn );
            info.playAddr = (uword)readHex( addrIn );
            if ( !addrIn )
            {
                return false;
            }
            hasAddress = true;
        }
        else if ( myStrNcaseCmp(cmpBuf,keyword_songs) == 0 )
        {
            istrstream numIn( cmpBuf + strlen(keyword_songs),
                              toolLen - strlen(keyword_songs) );
            if ( !numIn )
            {
                return false;
            }
            info.songs = (uword)readDec( numIn );
            info.startSong = (uword)readDec( numIn );
            hasSongs = true;
        }
        else if ( myStrNcaseCmp(cmpBuf,keyword_speed) == 0 )
        {
            istrstream speedIn( cmpBuf + strlen(keyword_speed),
                                toolLen - strlen(keyword_speed) );
            if ( !speedIn )
            {
                return false;
            }
            oldStyleSpeed = readHex(speedIn);
            hasSpeed = true;
        }
        else if ( myStrNcaseCmp(cmpBuf,keyword_name) == 0 )
        {
            strncpy( &infoString[0][0], cmpBuf + strlen(keyword_name), 31 );
            info.nameString = &infoString[0][0];
            info.infoString[0] = &infoString[0][0];
            hasName = true;
        }
        else if ( myStrNcaseCmp(cmpBuf,keyword_author) == 0 )
        {
            strncpy( &infoString[1][0], cmpBuf + strlen(keyword_author), 31 );
            info.authorString = &infoString[1][0];
            info.infoString[1] = &infoString[1][0];
            hasAuthor = true;
        }
        else if ( myStrNcaseCmp(cmpBuf,keyword_copyright) == 0 )
        {
            strncpy( &infoString[2][0], cmpBuf + strlen(keyword_copyright), 31 );
            info.copyrightString = &infoString[2][0];
            info.infoString[2] = &infoString[2][0];
            hasCopyright = true;
        }
        else if ( myStrNcaseCmp(cmpBuf,keyword_musPlayer) == 0 )
        {
            info.musPlayer = true;
        }
        else
        {
            hasUnknownChunk = true;
#if defined(SIDTUNE_REJECT_UNKNOWN_FIELDS)
            info.formatString = text_chunkError;
            return false;
#endif
        }
        // Skip to next tool.
        spTool += toolLen;
    }

    // Collected ``required'' information complete ?
    if ( hasAddress && hasName && hasAuthor && hasCopyright && hasSongs && hasSpeed )
    {
        // Create the speed/clock setting table.
        convertOldStyleSpeedToTables(oldStyleSpeed);
        if (( info.loadAddr == 0 ) && ( dataLength != 0 ))
        {
            smartPtr<const ubyte> spDataBuf((const ubyte*)dataBuffer,dataLength);
            spDataBuf += fileOffset;
            info.loadAddr = readEndian(spDataBuf[1],spDataBuf[0]);
            if ( !spDataBuf )
            {
                info.formatString = text_dataCorruptError;
                return false;
            }
            fileOffset += 2;
        }
        if ( info.initAddr == 0 )
        {
            info.initAddr = info.loadAddr;
        }
        info.numberOfInfoStrings = 3;
        // We finally accept the input data.
        info.formatString = text_format;
        return true;
    }
    else if ( hasAddress || hasName || hasAuthor || hasCopyright || hasSongs || hasSpeed )
    {
        // Something is missing (or damaged?).
        info.formatString = text_corruptError;
        return false;
    }
    else
    {
        // No PlaySID conform info strings.
        info.formatString = text_noStringsError;
        return false;
    }
}