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