1 // ----------------------------------------------------------------------------
2 // iso/cue/wav support
3 /*-----------------------------------------------------------------------------
4 Modified by: CaptainCPS-X
5 Updates:
6 (10/24/2011)
7 - removed libmad and MP3 support
8 - added my custom DirectSound library to add WAV support
9 - removed most (if not all) references to MP3
10 - modified a few other things as needed
11 ------------------------------------------------------------------------------*/
12 #include "burner.h"
13 #include "cdsound.h"
14
15 #define MAXIMUM_NUMBER_TRACKS (100)
16
17 #define CD_FRAMES_MINUTE (60 * 75)
18 #define CD_FRAMES_SECOND ( 75)
19 #define CD_FRAMES_PREGAP ( 2 * 75)
20
21 struct isowavTRACK_DATA {
22 char Control;
23 char TrackNumber;
24 char Address[4];
25 TCHAR* Filename;
26 };
27
28 struct isowavCDROM_TOC {
29 char FirstTrack;
30 char LastTrack;
31 isowavTRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
32 };
33
34 static isowavCDROM_TOC* isowavTOC;
35
36 static FILE* isowavFile = NULL;
37 static int isowavTrack = 0;
38 static int isowavLBA = 0;
39
40 // -----------------------------------------------------------------------------
41
isowavLBAToMSF(const int LBA)42 static const char* isowavLBAToMSF(const int LBA)
43 {
44 static char address[4];
45
46 address[0] = 0;
47 address[1] = LBA / CD_FRAMES_MINUTE;
48 address[2] = LBA % CD_FRAMES_MINUTE / CD_FRAMES_SECOND;
49 address[3] = LBA % CD_FRAMES_SECOND;
50
51 return address;
52 }
53
isowavMSFToLBA(const char * address)54 static int isowavMSFToLBA(const char* address)
55 {
56 int LBA;
57
58 LBA = address[3];
59 LBA += address[2] * CD_FRAMES_SECOND;
60 LBA += address[1] * CD_FRAMES_MINUTE;
61
62 return LBA;
63 }
64
65 // -----------------------------------------------------------------------------
66
isowavGetTrackSizes()67 static int isowavGetTrackSizes()
68 {
69 // determine the lenght of the .iso / .mp3 files to complete the TOC
70
71 FILE* h;
72
73 for (int i = isowavTOC->FirstTrack - 1; i < isowavTOC->LastTrack; i++) {
74
75 const char* address;
76
77 if (isowavTOC->TrackData[i].Control & 4) {
78
79 // data track
80
81 h = _tfopen(isowavTOC->TrackData[i].Filename, _T("rb"));
82 if (h == NULL) return 1;
83
84 fseek(h, 0, SEEK_END);
85
86 address = isowavLBAToMSF((ftell(h) + 2047) / 2048 + isowavMSFToLBA(isowavTOC->TrackData[i].Address));
87
88 if(h) fclose(h);
89
90 } else {
91
92 // audio track
93
94 h = _tfopen(isowavTOC->TrackData[i].Filename, _T("rb"));
95 if (h == NULL)return 1;
96
97 fseek(h, 0, SEEK_END);
98
99 address = isowavLBAToMSF(((ftell(h) + 2047) / 2048) + isowavMSFToLBA(isowavTOC->TrackData[i].Address));
100 if(h) fclose(h);
101 }
102
103 isowavTOC->TrackData[i + 1].Address[0] += 0; // always 0 [?]
104 isowavTOC->TrackData[i + 1].Address[1] += address[1]; // M
105 isowavTOC->TrackData[i + 1].Address[2] += address[2]; // S
106 isowavTOC->TrackData[i + 1].Address[3] += address[3]; // F
107 }
108
109 return 0;
110 }
111
isowavTestISO()112 static int isowavTestISO()
113 {
114 TCHAR fullname[MAX_PATH];
115 TCHAR* filename;
116 int length = 0;
117 int offset = 0;
118 int track = 2;
119 FILE* h;
120
121 _tcscpy(fullname, CDEmuImage);
122 length = _tcslen(fullname);
123
124 // assume CD-ROM mode1/2048 format
125
126 if (length <= 4 && (_tcscmp(_T(".iso"), fullname + length - 4) || _tcscmp(_T(".bin"), fullname + length - 4))) {
127 return 1;
128 }
129
130 // create a TOC with only the data track first
131
132 isowavTOC->FirstTrack = 1;
133 isowavTOC->LastTrack = 1;
134
135 isowavTOC->TrackData[0].TrackNumber = 1;
136
137 isowavTOC->TrackData[0].Address[1] = 0;
138 isowavTOC->TrackData[0].Address[2] = 2;
139 isowavTOC->TrackData[0].Address[3] = 0;
140
141 isowavTOC->TrackData[0].Filename = (TCHAR*)malloc((length + 1) * sizeof(TCHAR));
142 if (isowavTOC->TrackData[0].Filename == NULL) {
143 return 1;
144 }
145 _tcscpy(isowavTOC->TrackData[0].Filename, fullname);
146
147 isowavTOC->TrackData[0].Control = 4;
148
149 // if the filename has a number in it, try to find .mp3 tracks
150
151 filename = ExtractFilename(fullname);
152 offset = (filename - fullname) + length - 6;
153 while (offset >= 0 && fullname[offset] != _T('0') && fullname[offset + 1] != _T('1')) {
154 offset--;
155 }
156 if (offset < 0) {
157 return isowavGetTrackSizes();
158 }
159
160 _stprintf(fullname + length - 4, _T(".wav"));
161
162 while (1) {
163 fullname[offset] = _T('0') + track / 10; fullname[offset + 1] = _T('0') + track % 10;
164
165 if ((h = _tfopen(fullname, _T("rb"))) == NULL) {
166 break;
167 }
168 fclose(h);
169
170 isowavTOC->TrackData[track - 1].TrackNumber = track;
171
172 isowavTOC->TrackData[track - 1].Filename = (TCHAR*)malloc((length + 1) * sizeof(TCHAR));
173 if (isowavTOC->TrackData[track - 1].Filename == NULL) {
174 return 1;
175 }
176 _tcscpy(isowavTOC->TrackData[track - 1].Filename, fullname);
177
178 isowavTOC->LastTrack = track;
179
180 track++;
181 }
182
183 return isowavGetTrackSizes();
184 }
185
isowavParseCueFile()186 static int isowavParseCueFile()
187 {
188 TCHAR szLine[1024];
189 TCHAR szFile[1024];
190 TCHAR* s;
191 TCHAR* t;
192 FILE* h;
193 int track = 0;
194 int length;
195
196 isowavTOC->FirstTrack = 1;
197 isowavTOC->LastTrack = 1;
198
199 isowavTOC->TrackData[0].Address[1] = 0;
200 isowavTOC->TrackData[0].Address[2] = 2;
201 isowavTOC->TrackData[0].Address[3] = 0;
202
203 h = _tfopen(CDEmuImage, _T("rt"));
204 if (h == NULL) {
205 return 1;
206 }
207
208 while (1) {
209 if (_fgetts(szLine, sizeof(szLine), h) == NULL) {
210 break;
211 }
212
213 length = _tcslen(szLine);
214 // get rid of the linefeed at the end
215 while (length && (szLine[length - 1] == _T('\r') || szLine[length - 1] == _T('\n'))) {
216 szLine[length - 1] = 0;
217 length--;
218 }
219
220 s = szLine;
221
222 // file info
223 if ((t = LabelCheck(s, _T("FILE"))) != 0) {
224 s = t;
225
226 TCHAR* szQuote;
227
228 // read filename
229 QuoteRead(&szQuote, NULL, s);
230
231 _sntprintf(szFile, ExtractFilename(CDEmuImage) - CDEmuImage, _T("%s"), CDEmuImage);
232 _sntprintf(szFile + (ExtractFilename(CDEmuImage) - CDEmuImage), 1024 - (ExtractFilename(CDEmuImage) - CDEmuImage), _T("/%s"), szQuote);
233
234 continue;
235 }
236
237 // track info
238 if ((t = LabelCheck(s, _T("TRACK"))) != 0) {
239 s = t;
240
241 // track number
242 track = _tcstol(s, &t, 10);
243
244 if (track < 1 || track > MAXIMUM_NUMBER_TRACKS) {
245 fclose(h);
246 return 1;
247 }
248
249 if (track < isowavTOC->FirstTrack) {
250 isowavTOC->FirstTrack = track;
251 }
252 if (track > isowavTOC->LastTrack) {
253 isowavTOC->LastTrack = track;
254 }
255 isowavTOC->TrackData[track - 1].TrackNumber = track;
256
257 isowavTOC->TrackData[track - 1].Filename = (TCHAR*)malloc((_tcslen(szFile) + 1) * sizeof(TCHAR));
258 if (isowavTOC->TrackData[track - 1].Filename == NULL) {
259 fclose(h);
260 return 1;
261 }
262 _tcscpy(isowavTOC->TrackData[track - 1].Filename, szFile);
263
264 s = t;
265
266 // type of track
267
268 if ((t = LabelCheck(s, _T("MODE1/2048"))) != 0) {
269 isowavTOC->TrackData[track - 1].Control = 4;
270
271 continue;
272 }
273 if ((t = LabelCheck(s, _T("AUDIO"))) != 0) {
274 isowavTOC->TrackData[track - 1].Control = 0;
275
276 continue;
277 }
278
279 fclose(h);
280 return 1;
281 }
282
283 // pregap
284 if ((t = LabelCheck(s, _T("PREGAP"))) != 0) {
285 s = t;
286
287 int M, S, F;
288
289 // pregap M
290 M = _tcstol(s, &t, 10);
291 s = t + 1;
292 // pregap S
293 S = _tcstol(s, &t, 10);
294 s = t + 1;
295 // pregap F
296 F = _tcstol(s, &t, 10);
297
298 if (M < 0 || M > 100 || S < 0 || S > 59 || F < 0 || F > 74) {
299 fclose(h);
300 return 1;
301 }
302
303 isowavTOC->TrackData[track - 1].Address[1] = M;
304 isowavTOC->TrackData[track - 1].Address[2] = S;
305 isowavTOC->TrackData[track - 1].Address[3] = F;
306
307 continue;
308 }
309 }
310
311 fclose(h);
312
313 return isowavGetTrackSizes();
314 }
315
316 // -----------------------------------------------------------------------------
317
isowavExit()318 static int isowavExit()
319 {
320 wav_exit();
321
322 if (isowavFile) {
323 fclose(isowavFile);
324 isowavFile = NULL;
325 }
326
327 isowavTrack = 0;
328 isowavLBA = 0;
329
330 if (isowavTOC) {
331 for (int i = 0; i < MAXIMUM_NUMBER_TRACKS; i++) {
332 free(isowavTOC->TrackData[i].Filename);
333 }
334
335 free(isowavTOC);
336 isowavTOC = NULL;
337 }
338
339 return 0;
340 }
341
isowavInit()342 static int isowavInit()
343 {
344 wav_exit();
345
346 isowavTOC = (isowavCDROM_TOC*)malloc(sizeof(isowavCDROM_TOC));
347 if (isowavTOC == NULL) {
348 return 1;
349 }
350 memset(isowavTOC, 0, sizeof(isowavCDROM_TOC));
351
352 TCHAR* filename = ExtractFilename(CDEmuImage);
353
354 if (_tcslen(filename) < 4) {
355 return 1;
356 }
357
358 if (_tcscmp(_T(".cue"), filename + _tcslen(filename) - 4) == 0) {
359 if (isowavParseCueFile()) {
360 dprintf(_T("*** Couldn't parse .cue file\n"));
361 isowavExit();
362
363 return 1;
364 }
365 } else {
366 if (isowavTestISO()) {
367 dprintf(_T("*** Couldn't find .iso / .bin file\n"));
368 isowavExit();
369
370 return 1;
371 }
372 }
373
374 dprintf(_T(" CD image TOC read\n"));
375
376 for (int i = isowavTOC->FirstTrack - 1; i < isowavTOC->LastTrack; i++) {
377 dprintf(_T(" track %2i start %02i:%02i:%02i control 0x%02X %s\n"), isowavTOC->TrackData[i].TrackNumber, isowavTOC->TrackData[i].Address[1], isowavTOC->TrackData[i].Address[2], isowavTOC->TrackData[i].Address[3], isowavTOC->TrackData[i].Control, isowavTOC->TrackData[i].Filename);
378 }
379 dprintf(_T(" total running time %02i:%02i:%02i\n"), isowavTOC->TrackData[(int)isowavTOC->LastTrack].Address[1], isowavTOC->TrackData[(int)isowavTOC->LastTrack].Address[2], isowavTOC->TrackData[(int)isowavTOC->LastTrack].Address[3]);
380
381 CDEmuStatus = idle;
382
383 return 0;
384 }
385
GetIsoPath()386 TCHAR* GetIsoPath()
387 {
388 if(isowavTOC) {
389 return isowavTOC->TrackData[0].Filename;
390 }
391 return NULL;
392 }
393
isowavStop()394 static int isowavStop()
395 {
396 wav_stop();
397
398 if (isowavFile) {
399 fclose(isowavFile);
400 isowavFile = NULL;
401 }
402 CDEmuStatus = idle;
403 return 0;
404 }
405
isowavPlayLBA(int LBA)406 static int isowavPlayLBA(int LBA)
407 {
408 isowavLBA = LBA;
409
410 for (isowavTrack = isowavTOC->FirstTrack - 1; isowavTrack < isowavTOC->LastTrack; isowavTrack++) {
411 if (isowavLBA < isowavMSFToLBA(isowavTOC->TrackData[isowavTrack + 1].Address)) {
412 break;
413 }
414 }
415
416 if (isowavTrack >= isowavTOC->LastTrack) {
417 return 1;
418 }
419
420 bprintf(PRINT_IMPORTANT, _T(" playing track %2i - %s\n"), isowavTrack + 1, isowavTOC->TrackData[isowavTrack].Filename);
421
422 isowavFile = _tfopen(isowavTOC->TrackData[isowavTrack].Filename, _T("rb"));
423 if (isowavFile == NULL) {
424 return 1;
425 }
426
427 if( strstr(isowavTOC->TrackData[isowavTrack].Filename, _T(".wav")) || strstr(isowavTOC->TrackData[isowavTrack].Filename, _T(".WAV"))) {
428 // is a wav, no need to keep this file pointer
429 if (isowavFile) {
430 fclose(isowavFile);
431 isowavFile = NULL;
432 }
433
434 if(wav_open(isowavTOC->TrackData[isowavTrack].Filename)) {
435 wav_play();
436 } else {
437 // error creating the WAV stream
438 return 1;
439 }
440 }
441
442 //dprintf(_T("*** WAV: wBitsPerSample: %d \n"), wav->g_pStreamingSound->m_pWaveFile->m_pwfx->wBitsPerSample);
443 //dprintf(_T("*** WAV: nAvgBytesPerSec: %d \n"), wav->g_pStreamingSound->m_pWaveFile->m_pwfx->nAvgBytesPerSec);
444 //dprintf(_T("*** WAV: m_dwSize: %d \n"), wav->g_pStreamingSound->m_pWaveFile->m_dwSize);
445 //dprintf(_T("*** WAV: nBlockAlign: %d \n"), wav->g_pStreamingSound->m_pWaveFile->m_pwfx->nBlockAlign);
446
447 isowavLBA = isowavMSFToLBA(isowavTOC->TrackData[isowavTrack].Address);
448 CDEmuStatus = playing;
449
450 return 0;
451 }
452
isowavPlay(unsigned char M,unsigned char S,unsigned char F)453 static int isowavPlay(unsigned char M, unsigned char S, unsigned char F)
454 {
455 const char address[] = { 0, M, S, F };
456
457 return isowavPlayLBA(isowavMSFToLBA(address));
458 }
459
isowavLoadSector(int LBA,char * pBuffer)460 static int isowavLoadSector(int LBA, char* pBuffer)
461 {
462 LBA += CD_FRAMES_PREGAP;
463
464 if (LBA != isowavLBA) {
465
466 int track;
467
468 for (track = isowavTOC->FirstTrack - 1; track < isowavTOC->LastTrack; track++) {
469 if (LBA < isowavMSFToLBA(isowavTOC->TrackData[track + 1].Address)) {
470 break;
471 }
472 }
473
474 if (isowavFile == NULL || track != isowavTrack) {
475 isowavStop();
476
477 isowavTrack = track;
478
479 bprintf(PRINT_IMPORTANT, _T(" reading track %2i - %s\n"), isowavTrack + 1, isowavTOC->TrackData[isowavTrack].Filename);
480
481 isowavFile = _tfopen(isowavTOC->TrackData[isowavTrack].Filename, _T("rb"));
482 if (isowavFile == NULL) {
483 return 0;
484 }
485 }
486
487 if (fseek(isowavFile, (LBA - isowavMSFToLBA(isowavTOC->TrackData[isowavTrack].Address)) * 2048, SEEK_SET)) {
488 dprintf(_T("*** couldn't seek\n"));
489
490 return 0;
491 }
492
493 isowavLBA = (ftell(isowavFile) + 2047) / 2048;
494
495 CDEmuStatus = reading;
496 }
497
498 if (fread(pBuffer, 1, 2048, isowavFile) <= 0) {
499 dprintf(_T("*** couldn't read from file\n"));
500
501 isowavStop();
502
503 return 0;
504 }
505
506 isowavLBA = isowavMSFToLBA(isowavTOC->TrackData[isowavTrack].Address) + (ftell(isowavFile) + 2047) / 2048;
507
508 return isowavLBA - CD_FRAMES_PREGAP;
509 }
510
isowavReadTOC(int track)511 static unsigned char* isowavReadTOC(int track)
512 {
513 static unsigned char TOCEntry[4];
514
515 if (track == -1) {
516 TOCEntry[0] = isowavTOC->FirstTrack - 1;
517 TOCEntry[1] = isowavTOC->LastTrack;
518 TOCEntry[2] = 0;
519 TOCEntry[3] = 0;
520
521 return TOCEntry;
522 }
523 if (track == -2) {
524 TOCEntry[0] = isowavTOC->TrackData[(int)isowavTOC->LastTrack].Address[1];
525 TOCEntry[1] = isowavTOC->TrackData[(int)isowavTOC->LastTrack].Address[2];
526 TOCEntry[2] = isowavTOC->TrackData[(int)isowavTOC->LastTrack].Address[3];
527
528 TOCEntry[3] = 0;
529
530 return TOCEntry;
531 }
532
533 if (track >= isowavTOC->FirstTrack - 1 && track <= isowavTOC->LastTrack) {
534 TOCEntry[0] = isowavTOC->TrackData[track - 1].Address[1];
535 TOCEntry[1] = isowavTOC->TrackData[track - 1].Address[2];
536 TOCEntry[2] = isowavTOC->TrackData[track - 1].Address[3];
537 TOCEntry[3] = isowavTOC->TrackData[track - 1].Control;
538 }
539
540 return TOCEntry;
541 }
542
isowavReadQChannel()543 static unsigned char* isowavReadQChannel()
544 {
545 static unsigned char QChannelData[8];
546
547 switch (CDEmuStatus) {
548 case reading:
549 case playing: {
550 const char* AddressAbs = isowavLBAToMSF(isowavLBA);
551 const char* AddressRel = isowavLBAToMSF(isowavLBA - isowavMSFToLBA(isowavTOC->TrackData[isowavTrack].Address));
552
553 QChannelData[0] = isowavTOC->TrackData[isowavTrack].TrackNumber;
554
555 QChannelData[1] = AddressAbs[1];
556 QChannelData[2] = AddressAbs[2];
557 QChannelData[3] = AddressAbs[3];
558
559 QChannelData[4] = AddressRel[1];
560 QChannelData[5] = AddressRel[2];
561 QChannelData[6] = AddressRel[3];
562
563 QChannelData[7] = isowavTOC->TrackData[isowavTrack].Control;
564
565 break;
566 }
567 case paused: {
568 break;
569 }
570 default: {
571 memset(QChannelData, 0, sizeof(QChannelData));
572 }
573 }
574
575 return QChannelData;
576 }
577
isowavGetSoundBuffer(short *,int)578 static int isowavGetSoundBuffer(short* /*buffer*/, int /*samples*/)
579 {
580 // ---------------------------------------------------------------------
581 // TODO:
582 // Port the old 'isomp3GetSoundBuffer()' function from 'cd_isomp3.cpp'
583 // to use WAVE stream data, porting that function will fix the
584 // 00:00 progress status on the main NeoGeo CD BIOS menu.
585 // ---------------------------------------------------------------------
586
587 return 0;
588 }
589
isowavGetSettings(InterfaceInfo *)590 static int isowavGetSettings(InterfaceInfo* /*pInfo*/)
591 {
592 return 0;
593 }
594
595 struct CDEmuDo isowavDo = { isowavExit, isowavInit, isowavStop, isowavPlay, isowavLoadSector, isowavReadTOC, isowavReadQChannel, isowavGetSoundBuffer, isowavGetSettings, _T("cue/iso/wav CD emulation") };
596