/[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 1075 - (show annotations)
Sun Jan 12 20:13:41 2014 UTC (5 years, 11 months ago) by dbug
File size: 11872 byte(s)
PictConv 0.016
- New option to skip the conversion if the target file is more recent than the source file (-u)
- New option to enable/disable information about what happens (-v)

Ym2Mym 1.1
- The tool is now able to extract LHA compressed YM files directly, should make the process much easier :)
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,FILE *f);
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\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 );
68
69 int retune_music=1;
70
71 ArgumentParser argumentParser(argc,argv);
72
73 while (argumentParser.ProcessNextArgument())
74 {
75 if (argumentParser.IsSwitch("-t"))
76 {
77 //format: [-t]
78 // 0 => No retune
79 // 1 => Double frequency [default]
80 retune_music=argumentParser.GetIntegerValue(0);
81 }
82 }
83
84 if (argumentParser.GetParameterCount()!=2)
85 {
86 ShowError(0);
87 }
88
89 unsigned char*data[REGS]; // The unpacked YM data
90 unsigned current[REGS];
91
92 char ym_new=0;
93
94 long n,i,row,change,pack,biggest=0,hits,oldrow,remain,offi;
95 long regbits[REGS]={8,4,8,4, 8,4,5,8, 5,5,5,8, 8,8}; // Bits per PSG reg
96 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
97
98 unsigned long length; // Song length
99
100 const char* sourceFilename(argumentParser.GetParameter(0));
101 void* pcBuffer;
102 size_t cBufferSize;
103 if (!LoadFile(sourceFilename,pcBuffer,cBufferSize))
104 {
105 ShowError("Can't load '%s'",sourceFilename);
106 }
107
108 const lzhHeader_t* header=(const lzhHeader_t*)pcBuffer;
109 if ( (cBufferSize>=sizeof(lzhHeader_t)) && (!strncmp(header->id,"-lh5-",5)) )
110 {
111 CLzhDepacker depacker;
112 int dstSize=header->original;
113 void *pDst=malloc(dstSize);
114 const char* packedData=(const char*)pcBuffer+sizeof(lzhHeader_t)+header->name_lenght+2;
115
116 if (!depacker.LzUnpack(packedData,header->packed,pDst,dstSize))
117 {
118 ShowError("Failed depacking '%s', wrong LHA format or corrupted file?",sourceFilename);
119 }
120 free(pcBuffer);
121 pcBuffer=pDst;
122 cBufferSize=dstSize;
123 }
124 const char* sourceData=(const char*)pcBuffer;
125
126 // Check if the file is compressed
127 length=cBufferSize-4;
128 if (!strncmp(sourceData,"YM2!",4)) // YM2 is ok
129 {
130 sourceData+=4;
131 // YM2!
132 // First four bytes is the ASCII identifier "YM2!".
133 }
134 else
135 if (!strncmp(sourceData,"YM3!",4)) // YM3 is ok
136 {
137 sourceData+=4;
138 // YM3!
139 // First four bytes is again the ASCII identifier "YM3!".
140 // ------------------------------------------------------
141 // Offset Size Name Value Contents
142 // ------------------------------------------------------
143 // 0 4 ID "YM3!" File type Identificator
144 // The next bytes are the data block of AY chip registers values.
145 //
146 // Registers are updates one time per VBL interrupt. If music length is N interrupts,
147 // then block consist first N bytes for register 0, further N bytes for register 1
148 // and so on. In total: N*14 bytes. The number of used VBL for music can be computed
149 // as follow: nvbl = (ymfile_size-4)/14;
150 //
151 // VBL1:
152 // store reg0,reg1,reg2,...,reg12,reg13 (14 regs)
153 // VBL2:
154 // store reg0,reg1,reg2,...,reg12,reg13 (14 regs)
155 // ..........
156 // VBLn:
157 // store reg0,reg1,reg2,...,reg12,reg13 (14 regs)
158 //
159 // If the current interrupt features no output to register 13 then the byte of the
160 // data block for this interrupt and for this register has the value 255 ($FF).
161 }
162 else
163 if (!strncmp(sourceData,"YM3b",4)) // YM3b is ok
164 {
165 sourceData+=4;
166 // YM3b!
167 //
168 // This format is nearly identical with YM3. It adds only the ability to use loops.
169 //
170 // First four bytes is the ASCII identifier "YM3b".
171 // The following bytes are the data block (see YM3 description).
172 // Last four bytes is a DWORD (32bits integers) data and contains the frame number
173 // at which the loop restart. That's all.
174 sourceData+=4; // Skip restart for YM3b
175 length-=4;
176 }
177 else
178 if (!strncmp(sourceData,"YM4!",4)) // YM4 is not yet ok
179 {
180 sourceData+=4;
181 // YM4!
182 // (note, should be similar to YM5, without extra infos)
183 printf("YM4! format is not yet supported.\n");
184 exit(EXIT_FAILURE);
185 }
186 else
187 if ( (!strncmp(sourceData,"YM5!",4)) || (!strncmp(sourceData,"YM6!",4)) ) // YM5 is ok but needs a different loader
188 {
189 sourceData+=4;
190 // YM5!
191 // This is the actual and most common used format and consist consists of additional information:
192 // chip frequency, player frequency, title, author name, comment and specific Atari ST data (Digi-Drum and SID effects).
193
194 // YM6!
195 // This format is equivalent to YM5! but can use yet another special Atari effect.
196 ym_new=1;
197 }
198 else
199 {
200 printf("Unknown file format '%s'.\n",sourceData);
201 exit(EXIT_FAILURE);
202 }
203
204 if (ym_new) // New YM5 format loader
205 {
206 sourceData+=8; // Skip 'LeOnArD' checkstring
207 for (n=length=0;n<4;n++) // Number of VBL's
208 {
209 length<<=8;
210 length+=*sourceData++;
211 }
212 length*=REGS;
213
214 sourceData+=3; // Skip first 3 bytes of info
215 if (!((*sourceData++)&1))
216 {
217 printf("Only interleaved data supported.\n");
218 return(EXIT_FAILURE);
219 }
220
221 if ((*sourceData++) || (*sourceData++)) // Number of digidrums
222 {
223 printf("Digidrums not supported.\n");
224 return(EXIT_FAILURE);
225 }
226
227 sourceData+=4; /* Skip external freq */
228 sourceData+=2; /* Skip VBL freq */
229 sourceData+=4; /* Skip loop position */
230 sourceData+=2; /* Skip additional data */
231
232 while((*sourceData++)) /* Skip song name */
233 ;
234 while((*sourceData++)) /* Skip author name */
235 ;
236 while((*sourceData++)) /* Skip comments */
237 ;
238 }
239
240 /* Old YM2/YM3 format loader */
241 for (n=0;n<REGS;n++) /* Allocate memory & read data */
242 {
243 /* Allocate extra fragment to make packing easier */
244 if ((data[n]=(unsigned char*)malloc(length/REGS+FRAG))==NULL)
245 {
246 printf("Out of memory.\n");
247 return(EXIT_FAILURE);
248 }
249 memset(data[n],0,length/REGS+FRAG);
250 memcpy(data[n],sourceData,length/REGS);
251 sourceData+=length/REGS;
252 }
253
254 if (retune_music)
255 {
256 unsigned int frame;
257 printf("Retuning the frequency.\n");
258 for (frame=0;frame<=length/REGS;frame++)
259 {
260 int freqA= ( ((data[1][frame])<<8) | (data[0][frame]) )>>1;
261 int freqB= ( ((data[3][frame])<<8) | (data[2][frame]) )>>1;
262 int freqC= ( ((data[5][frame])<<8) | (data[4][frame]) )>>1;
263 int freqEnv= ( ((data[12][frame])<<8) | (data[11][frame]) )>>1;
264 int freqNoise= data[6][frame]>>1;
265
266 data[0][frame]=freqA & 255;
267 data[1][frame]=(freqA>>8);
268
269 data[2][frame]=freqB & 255;
270 data[3][frame]=(freqB>>8);
271
272 data[4][frame]=freqC & 255;
273 data[5][frame]=(freqC>>8);
274
275 data[6][frame]=freqNoise;
276
277 data[11][frame]=freqEnv & 255;
278 data[12][frame]=(freqEnv>>8);
279 }
280 }
281
282
283 if (ym_new) // Let's mask the extra YM5 data out
284 {
285 for (n=0;n<REGS;n++)
286 {
287 for (row=0;row<length/REGS;row++)
288 {
289 data[n][row]&=regand[n];
290 }
291 }
292 }
293
294 FILE* f;
295 if ((f=fopen(argumentParser.GetParameter(1),"wb"))==NULL)
296 {
297 printf("Cannot open destination file.\n");
298 return(EXIT_FAILURE);
299 }
300
301 // Set current values to impossible
302 for (n=0;n<REGS;n++)
303 {
304 current[n]=0xffff;
305 }
306
307 fputc(length/REGS&0xff,f); // Write tune length
308 fputc(length/REGS>>8,f);
309
310 for (n=0;n<length/REGS;n+=FRAG) // Go through fragments...
311 {
312 for (i=0;i<REGS;i++) // ... for each register
313 {
314 for (row=change=0;row<FRAG;row++)
315 if (data[i][n+row]!=current[i])
316 change=1;
317
318 if (!change) // No changes in the whole fragment
319 {
320 writebits(0,1,f);
321 continue; // Skip the next pass
322 }
323 else
324 {
325 writebits(1,1,f);
326 }
327
328 for (row=0;row<FRAG;row++)
329 {
330 if (data[i][n+row]!=current[i])
331 {
332 change=1;
333 current[i]=data[i][n+row];
334
335 biggest=0;
336 if (n) // Skip first fragment
337 {
338 offi=0;
339 remain=FRAG-row;
340
341 // Go through the preceding data and try to find similar data
342 for (oldrow=0;oldrow<FRAG;oldrow++)
343 {
344 hits=0;
345 for (pack=0;pack<remain;pack++)
346 {
347 if (data[i][n+row+pack]==data[i][n-FRAG+row+oldrow+pack]
348 && oldrow+pack<FRAG)
349 hits++;
350 else
351 break;
352 }
353 if (hits>biggest) // Bigger sequence found
354 {
355 biggest=hits;
356 offi=oldrow;
357 }
358 }
359 }
360
361 if (biggest>1) // Could we pack data?
362 {
363 row+=biggest-1;
364 current[i]=data[i][n+row];
365 writebits(2,2,f);
366 writebits((offi<<OFFNUM/2)+(biggest-1),OFFNUM,f);
367 }
368 else // Nope, write raw bits
369 {
370 writebits(3,2,f);
371 writebits(data[i][n+row],regbits[i],f);
372 }
373 }
374 else // Same as former value, write 0
375 {
376 writebits(0,1,f);
377 }
378 }
379 }
380 }
381
382 writebits(0,0,f); // Pad to byte size
383 fclose(f);
384
385 free(pcBuffer);
386
387 return EXIT_SUCCESS;
388 }
389
390 // Writes bits to a file. If bits is 0, pads to byte size.
391 void writebits(unsigned data,int bits,FILE *f)
392 {
393 static unsigned char byte=0;
394
395 static int off=0;
396
397 int n;
398
399 if (!bits && off)
400 {
401 off=byte=0;
402 fputc(byte,f);
403 return;
404 }
405
406 // Go through the bits and write a whole byte if needed
407 for (n=0;n<bits;n++)
408 {
409 if (data&(1<<bits-1-n))
410 byte|=0x80>>off;
411
412 if (++off==8)
413 {
414 fputc(byte,f);
415 off=byte=0;
416 }
417 }
418 }
419

  ViewVC Help
Powered by ViewVC 1.1.26