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

  ViewVC Help
Powered by ViewVC 1.1.26