/[projet1]/public/pc/tools/osdk/main/Ym2Mym/sources/Ym2Mym.cpp
Defence Force logotype

Contents of /public/pc/tools/osdk/main/Ym2Mym/sources/Ym2Mym.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1202 - (show annotations)
Sat Sep 20 16:51:37 2014 UTC (5 years ago) by dbug
File size: 15029 byte(s)
Missing include for ptrdiff_t
1 /*
2 ym2mym.cpp
3
4 Converts _unpacked_ YM tunes to a format better suitable for
5 MSX1. Supports YM2 and YM3 types well plus YM5 somehow.
6
7 30.1.2000 Marq/Lieves!Tuore & Fit (marq@iki.fi)
8
9 3.2.2000 - Added a rude YM5 loader. Skips most of the header.
10
11 Output format:
12
13 Rows in the tune, 16 bits (lobyte first)
14 For each register, 0 - fragment contains only unchanged data
15 1 - fragment contains packed data
16
17 In a packed fragment, 0 - register value is the same as before
18 11 - raw register data follows. Only regbits[i]
19 bits, not full 8
20 10 - offset + number of bytes from preceding
21 data. As many bits as are required to hold
22 fragment offset & counter data (OFFNUM).
23
24 AYC information: http://www.cpcwiki.eu/index.php/AYC
25
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stddef.h>
32
33 #include "infos.h"
34 #include "common.h"
35 #include "lzh.h"
36
37 #define REGS 14
38 #define FRAG 128 // Number of rows to compress at a time
39 #define OFFNUM 14 // Bits needed to store off+num of FRAG
40
41
42
43 void writebits(unsigned data,int bits,char* &ptrWrite);
44
45 int main(int argc,char *argv[])
46 {
47 //
48 // Some initialization for the common library
49 //
50 SetApplicationParameters(
51 "Ym2Mym",
52 TOOL_VERSION_MAJOR,
53 TOOL_VERSION_MINOR,
54 "{ApplicationName} - Version {ApplicationVersion} - This program is a part of the OSDK\r\n"
55 "\r\n"
56 "Author:\r\n"
57 " First version by Marq/Lieves!Tuore & Fit (marq@iki.fi) \r\n"
58 " More recent updates by Pointier Mickael \r\n"
59 "\r\n"
60 "Purpose:\r\n"
61 " Convert Atari ST/Amstrad musics from the YM to MYM format.\r\n"
62 "\r\n"
63 "Usage:\r\n"
64 " {ApplicationName} source.ym destination.mym [load adress] [header name] \r\n"
65 "\r\n"
66 "Switches:\r\n"
67 " -tn Tuning\r\n"
68 " -t0 => No retune\r\n"
69 " -t1 => Double frequency [default]\r\n"
70 "\r\n"
71 " -hn Header\r\n"
72 " -h0 => No tape header [default]\r\n"
73 " -h1 => Use tape header (requires a start address and a name)\r\n"
74 "\r\n"
75 " -vn Verbosity.\r\n"
76 " -v0 => Silent [default]\r\n"
77 " -v1 => Shows information about what Ym2Mym is doing\r\n"
78 "\r\n"
79 " -mn Max size.\r\n"
80 " -m0 => No size limit [default]\r\n"
81 " -m1234 => Outputs an error if the exported size is too large\r\n"
82 "\r\n"
83 );
84
85 int maxSize=0;
86 int retune_music=1;
87 bool flagVerbosity=false;
88 bool flag_header=false;
89
90 ArgumentParser argumentParser(argc,argv);
91
92 while (argumentParser.ProcessNextArgument())
93 {
94 if (argumentParser.IsSwitch("-t"))
95 {
96 //format: [-t]
97 // 0 => No retune
98 // 1 => Double frequency [default]
99 retune_music=argumentParser.GetIntegerValue(0);
100 }
101 else
102 if (argumentParser.IsSwitch("-v"))
103 {
104 //testing: [-v]
105 // 0 => silent
106 // 1 => verbose
107 flagVerbosity=argumentParser.GetBooleanValue(true);
108 }
109 else
110 if (argumentParser.IsSwitch("-h"))
111 {
112 //format: [-h]
113 // 0 => suppress header
114 // 1 => save header (default)
115 flag_header=argumentParser.GetBooleanValue(true);
116 }
117 else
118 if (argumentParser.IsSwitch("-m"))
119 {
120 //format: [-m]
121 // 0 => no max size (default)
122 // other => maximum size
123 maxSize=argumentParser.GetIntegerValue(0);
124 }
125 }
126
127 int adress_start=0;
128 std::string headerName;
129
130 if (flag_header)
131 {
132 // Header mode: Four parameters
133 if (argumentParser.GetParameterCount()!=4)
134 {
135 //
136 // Wrong number of arguments
137 //
138 ShowError(0);
139 }
140
141 //
142 // Check start address
143 //
144 adress_start=ConvertAdress(argumentParser.GetParameter(2));
145 headerName=argumentParser.GetParameter(3);
146 }
147 else
148 {
149 // No header mode: Two parameters
150 if (argumentParser.GetParameterCount()!=2)
151 {
152 ShowError(0);
153 }
154 }
155
156 unsigned char*data[REGS]; // The unpacked YM data
157 unsigned current[REGS];
158
159 char ym_new=0;
160
161 long n,row;
162 long i,change,pack,biggest=0,hits,oldrow,remain,offi;
163 long regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}; // Bits per PSG reg
164 long regand[REGS]={255,15,255,15, 255,15,31,255, 31,31,31,255, 255,255}; // AND values to mask out extra bits from register data
165
166 unsigned long length; // Song length
167
168 const char* sourceFilename(argumentParser.GetParameter(0));
169 void* pcBuffer;
170 size_t cBufferSize;
171 if (!LoadFile(sourceFilename,pcBuffer,cBufferSize))
172 {
173 ShowError("Can't load '%s'",sourceFilename);
174 }
175
176 const lzhHeader_t* header=(const lzhHeader_t*)pcBuffer;
177 if ( (cBufferSize>=sizeof(lzhHeader_t)) && (!strncmp(header->id,"-lh5-",5)) )
178 {
179 CLzhDepacker depacker;
180 int dstSize=header->original;
181 void *pDst=malloc(dstSize);
182 const char* packedData=(const char*)pcBuffer+sizeof(lzhHeader_t)+header->name_lenght+2;
183
184 if (!depacker.LzUnpack(packedData,header->packed,pDst,dstSize))
185 {
186 ShowError("Failed depacking '%s', wrong LHA format or corrupted file?",sourceFilename);
187 }
188 free(pcBuffer);
189 pcBuffer=pDst;
190 cBufferSize=dstSize;
191 }
192 const unsigned char* sourceData=(const unsigned char*)pcBuffer;
193
194 // Check if the file is compressed
195 length=cBufferSize-4;
196 if (!strncmp((const char*)sourceData,"YM2!",4)) // YM2 is ok
197 {
198 sourceData+=4;
199 // YM2!
200 // First four bytes is the ASCII identifier "YM2!".
201 }
202 else
203 if (!strncmp((const char*)sourceData,"YM3!",4)) // YM3 is ok
204 {
205 sourceData+=4;
206 // YM3!
207 // First four bytes is again the ASCII identifier "YM3!".
208 // ------------------------------------------------------
209 // Offset Size Name Value Contents
210 // ------------------------------------------------------
211 // 0 4 ID "YM3!" File type Identificator
212 // The next bytes are the data block of AY chip registers values.
213 //
214 // Registers are updates one time per VBL interrupt. If music length is N interrupts,
215 // then block consist first N bytes for register 0, further N bytes for register 1
216 // and so on. In total: N*14 bytes. The number of used VBL for music can be computed
217 // as follow: nvbl = (ymfile_size-4)/14;
218 //
219 // VBL1:
220 // store reg0,reg1,reg2,...,reg12,reg13 (14 regs)
221 // VBL2:
222 // store reg0,reg1,reg2,...,reg12,reg13 (14 regs)
223 // ..........
224 // VBLn:
225 // store reg0,reg1,reg2,...,reg12,reg13 (14 regs)
226 //
227 // If the current interrupt features no output to register 13 then the byte of the
228 // data block for this interrupt and for this register has the value 255 ($FF).
229 }
230 else
231 if (!strncmp((const char*)sourceData,"YM3b",4)) // YM3b is ok
232 {
233 sourceData+=4;
234 // YM3b!
235 //
236 // This format is nearly identical with YM3. It adds only the ability to use loops.
237 //
238 // First four bytes is the ASCII identifier "YM3b".
239 // The following bytes are the data block (see YM3 description).
240 // Last four bytes is a DWORD (32bits integers) data and contains the frame number
241 // at which the loop restart. That's all.
242 sourceData+=4; // Skip restart for YM3b
243 length-=4;
244 }
245 else
246 if (!strncmp((const char*)sourceData,"YM4!",4)) // YM4 is not yet ok
247 {
248 sourceData+=4;
249 // YM4!
250 // (note, should be similar to YM5, without extra infos)
251 printf("YM4! format is not yet supported.\n");
252 exit(EXIT_FAILURE);
253 }
254 else
255 if ( (!strncmp((const char*)sourceData,"YM5!",4)) || (!strncmp((const char*)sourceData,"YM6!",4)) ) // YM5 is ok but needs a different loader
256 {
257 sourceData+=4;
258 // YM5!
259 // This is the actual and most common used format and consist consists of additional information:
260 // chip frequency, player frequency, title, author name, comment and specific Atari ST data (Digi-Drum and SID effects).
261
262 // YM6!
263 // This format is equivalent to YM5! but can use yet another special Atari effect.
264 ym_new=1;
265 }
266 else
267 {
268 printf("Unknown file format '%s'.\n",sourceData);
269 exit(EXIT_FAILURE);
270 }
271
272 if (ym_new) // New YM5 format loader
273 {
274 sourceData+=8; // Skip 'LeOnArD' checkstring
275 for (n=length=0;n<4;n++) // Number of VBL's
276 {
277 length<<=8;
278 length+=*sourceData++;
279 }
280 length*=REGS;
281
282 sourceData+=3; // Skip first 3 bytes of info
283 if (!((*sourceData++)&1))
284 {
285 printf("Only interleaved data supported.\n");
286 return(EXIT_FAILURE);
287 }
288
289 if ((*sourceData++) || (*sourceData++)) // Number of digidrums
290 {
291 printf("Digidrums not supported.\n");
292 return(EXIT_FAILURE);
293 }
294
295 sourceData+=4; /* Skip external freq */
296 sourceData+=2; /* Skip VBL freq */
297 sourceData+=4; /* Skip loop position */
298 sourceData+=2; /* Skip additional data */
299
300 while((*sourceData++)) /* Skip song name */
301 ;
302 while((*sourceData++)) /* Skip author name */
303 ;
304 while((*sourceData++)) /* Skip comments */
305 ;
306 }
307
308 /* Old YM2/YM3 format loader */
309 for (n=0;n<REGS;n++) /* Allocate memory & read data */
310 {
311 /* Allocate extra fragment to make packing easier */
312 if ((data[n]=(unsigned char*)malloc(length/REGS+FRAG))==NULL)
313 {
314 printf("Out of memory.\n");
315 return(EXIT_FAILURE);
316 }
317 memset(data[n],0,length/REGS+FRAG);
318 memcpy(data[n],sourceData,length/REGS);
319 sourceData+=length/REGS;
320 }
321
322 if (retune_music)
323 {
324 unsigned int frame;
325 if (flagVerbosity)
326 {
327 printf("Retuning the frequency.\n");
328 }
329 for (frame=0;frame<=length/REGS;frame++)
330 {
331 int freqA= ( ((data[1][frame])<<8) | (data[0][frame]) )>>1;
332 int freqB= ( ((data[3][frame])<<8) | (data[2][frame]) )>>1;
333 int freqC= ( ((data[5][frame])<<8) | (data[4][frame]) )>>1;
334 int freqEnv= ( ((data[12][frame])<<8) | (data[11][frame]) )>>1;
335 int freqNoise= data[6][frame]>>1;
336
337 data[0][frame]=freqA & 255;
338 data[1][frame]=(freqA>>8);
339
340 data[2][frame]=freqB & 255;
341 data[3][frame]=(freqB>>8);
342
343 data[4][frame]=freqC & 255;
344 data[5][frame]=(freqC>>8);
345
346 data[6][frame]=freqNoise;
347
348 data[11][frame]=freqEnv & 255;
349 data[12][frame]=(freqEnv>>8);
350 }
351 }
352
353
354 if (ym_new) // Let's mask the extra YM5 data out
355 {
356 for (n=0;n<REGS;n++)
357 {
358 for (row=0;row<(long)(length/REGS);row++)
359 {
360 data[n][row]&=regand[n];
361 }
362 }
363 }
364
365
366 char* destinationBuffer=(char*)malloc(cBufferSize);
367 char* ptrWrite=destinationBuffer;
368
369 // Set current values to impossible
370 for (n=0;n<REGS;n++)
371 {
372 current[n]=0xffff;
373 }
374
375 *ptrWrite++=length/REGS&0xff; // Write tune length
376 *ptrWrite++=(length/REGS>>8)&255;
377
378 for (n=0;n<(long)(length/REGS);n+=FRAG) // Go through fragments...
379 {
380 for (i=0;i<REGS;i++) // ... for each register
381 {
382 for (row=change=0;row<FRAG;row++)
383 if (data[i][n+row]!=current[i])
384 change=1;
385
386 if (!change) // No changes in the whole fragment
387 {
388 writebits(0,1,ptrWrite);
389 continue; // Skip the next pass
390 }
391 else
392 {
393 writebits(1,1,ptrWrite);
394 }
395
396 for (row=0;row<FRAG;row++)
397 {
398 if (data[i][n+row]!=current[i])
399 {
400 change=1;
401 current[i]=data[i][n+row];
402
403 biggest=0;
404 if (n) // Skip first fragment
405 {
406 offi=0;
407 remain=FRAG-row;
408
409 // Go through the preceding data and try to find similar data
410 for (oldrow=0;oldrow<FRAG;oldrow++)
411 {
412 hits=0;
413 for (pack=0;pack<remain;pack++)
414 {
415 if (data[i][n+row+pack]==data[i][n-FRAG+row+oldrow+pack]
416 && oldrow+pack<FRAG)
417 hits++;
418 else
419 break;
420 }
421 if (hits>biggest) // Bigger sequence found
422 {
423 biggest=hits;
424 offi=oldrow;
425 }
426 }
427 }
428
429 if (biggest>1) // Could we pack data?
430 {
431 row+=biggest-1;
432 current[i]=data[i][n+row];
433 writebits(2,2,ptrWrite);
434 writebits((offi<<OFFNUM/2)+(biggest-1),OFFNUM,ptrWrite);
435 }
436 else // Nope, write raw bits
437 {
438 writebits(3,2,ptrWrite);
439 writebits(data[i][n+row],regbits[i],ptrWrite);
440 }
441 }
442 else // Same as former value, write 0
443 {
444 writebits(0,1,ptrWrite);
445 }
446 }
447 }
448 }
449
450 writebits(0,0,ptrWrite); // Pad to byte size
451
452 ptrdiff_t outputFileSize=(ptrWrite-destinationBuffer);
453
454 if (maxSize && (outputFileSize>maxSize))
455 {
456 ShowError("File '%s' is too large (%d bytes instead of maximum %d)",sourceFilename,outputFileSize,maxSize);
457 }
458
459 FILE* f;
460 if ((f=fopen(argumentParser.GetParameter(1),"wb"))==NULL)
461 {
462 printf("Cannot open destination file.\n");
463 return(EXIT_FAILURE);
464 }
465
466 if (flag_header)
467 {
468 //
469 // Write tape header
470 //
471 unsigned char Header[]=
472 {
473 0x16, // 0 Synchro
474 0x16, // 1
475 0x16, // 2
476 0x24, // 3
477
478 0x00, // 4
479 0x00, // 5
480
481 0x80, // 6
482
483 0x00, // 7 $80=BASIC Autostart $C7=Assembly autostart
484
485 0xbf, // 8 End address
486 0x40, // 9
487
488 0xa0, // 10 Start address
489 0x00, // 11
490
491 0x00 // 12
492 };
493
494 int adress_end =adress_start+outputFileSize-1;
495
496 Header[10]=(adress_start>>8);
497 Header[11]=(adress_start&255);
498
499 Header[8]=(adress_end>>8);
500 Header[9]=(adress_end&255);
501
502 //
503 // Write header
504 //
505 fwrite(Header,sizeof(Header),1,f);
506 fwrite(headerName.c_str(),headerName.size()+1,1,f); // Include the null terminator
507 }
508 fwrite(destinationBuffer,outputFileSize,1,f);
509
510 fclose(f);
511
512 free(pcBuffer);
513 free(destinationBuffer);
514
515 return EXIT_SUCCESS;
516 }
517
518 // Writes bits to a file. If bits is 0, pads to byte size.
519 void writebits(unsigned data,int bits,char* &ptrWrite)
520 {
521 static unsigned char byte=0;
522
523 static int off=0;
524
525 int n;
526
527 if (!bits && off)
528 {
529 off=byte=0;
530 *ptrWrite++=byte;
531 return;
532 }
533
534 // Go through the bits and write a whole byte if needed
535 for (n=0;n<bits;n++)
536 {
537 if (data&(1<<((bits-1)-n)))
538 byte|=0x80>>off;
539
540 if (++off==8)
541 {
542 *ptrWrite++=byte;
543 off=byte=0;
544 }
545 }
546 }
547

  ViewVC Help
Powered by ViewVC 1.1.26