From e1a4d998525938784faf320de2191d47b86c5cbe Mon Sep 17 00:00:00 2001 From: taoenwen <67533945+taoenwen@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:40:07 +0800 Subject: [PATCH] =?UTF-8?q?Step=202:=20Globalize=20cdlist.cpp=20to=20repla?= =?UTF-8?q?ce=20platform=E2=80=91specific=20neocdlist.cpp=20implementation?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- makefile.burner_win32_rules | 2 +- makefile.pi | 4 +- makefile.sdl | 2 +- makefile.sdl2 | 2 +- projectfiles/qtcreator/fba_qt.pro | 28 +- projectfiles/qtcreator/fba_sdl.pro | 28 +- .../visualstudio-2015/fbneo_vs2015.vcxproj | 4 +- .../fbneo_vs2015.vcxproj.filters | 10 +- .../visualstudio-2019/fbneo_vs2019.vcxproj | 4 +- .../fbneo_vs2019.vcxproj.filters | 10 +- .../visualstudio-2022/fbneo_vs2022.vcxproj | 4 +- .../fbneo_vs2022.vcxproj.filters | 10 +- .../visualstudio-2026/fbneo_vs2026.vcxproj | 4 +- .../fbneo_vs2026.vcxproj.filters | 10 +- src/burner/cdlist.cpp | 590 ++++++ src/burner/cdlist.h | 56 + src/burner/neocdlist.h | 145 +- src/burner/pcecdlist_games.h | 1655 +++++++++++++++++ src/burner/qt/meson.build | 3 +- src/burner/qt/neocdlist.cpp | 393 ---- src/burner/sdl/meson.build | 2 +- src/burner/sdl/neocdlist.cpp | 409 ---- src/burner/win32/burner_win32.h | 3 - src/burner/win32/meson.build | 3 +- src/burner/win32/neocdlist.cpp | 680 ------- src/burner/win32/neocdsel.cpp | 507 +---- src/burner/win32/scrn.cpp | 18 + src/intf/cd/cd_chd.cpp | 48 +- src/intf/cd/cd_chd.h | 45 +- src/intf/cd/cd_img.cpp | 887 +++++++-- src/intf/cd/cd_img.h | 39 + 31 files changed, 3332 insertions(+), 2273 deletions(-) create mode 100644 src/burner/cdlist.cpp create mode 100644 src/burner/cdlist.h create mode 100644 src/burner/pcecdlist_games.h delete mode 100644 src/burner/qt/neocdlist.cpp delete mode 100644 src/burner/sdl/neocdlist.cpp delete mode 100644 src/burner/win32/neocdlist.cpp diff --git a/makefile.burner_win32_rules b/makefile.burner_win32_rules index e2b53df3e..9d8ad934a 100644 --- a/makefile.burner_win32_rules +++ b/makefile.burner_win32_rules @@ -4,7 +4,7 @@ alldir += burner burner/win32 dep/kaillera/client dep/libs/libspng dep/libs/lib depobj += about.o bzip.o choose_monitor.o cona.o debugger.o drv.o d3dkmt_sync.o fba_kaillera.o gameinfo.o image_win32.o \ inpc.o inpcheat.o inpd.o inpdipsw.o inps.o ips_manager.o localise.o localise_download.o localise_gamelist.o main.o \ - media.o memcard.o menu.o misc_win32.o neocdlist.o neocdsel.o numdial.o paletteviewer.o placeholder.o popup_win32.o \ + media.o memcard.o menu.o misc_win32.o cdlist.o neocdsel.o numdial.o paletteviewer.o placeholder.o popup_win32.o \ progress.o replay.o res.o roms.o run.o scrn.o sel.o sfactd.o splash.o stated.o support_paths.o systeminfo.o wave.o \ romdata.o mbtwc.o zip.o \ \ diff --git a/makefile.pi b/makefile.pi index 1680336f6..257e48054 100755 --- a/makefile.pi +++ b/makefile.pi @@ -49,7 +49,7 @@ alldir += burner burner/pi burner/sdl dep/libs/libspng dep/libs/lib7z \ intf/perfcount intf/perfcount/pi dep/generated \ dep/pi/gles -depobj += neocdlist.o \ +depobj += cdlist.o \ \ conc.o cong.o dat.o gamc.o gami.o image.o ioapi.o misc.o \ sshot.o state.o statec.o unzip.o zipfn.o \ @@ -269,7 +269,7 @@ else endif # For zlib -DEF := $(DEF) -DNO_VIZ -D_LARGEFILE64_SOURCE=0 -D_FILE_OFFSET_BITS=64 +DEF := $(DEF) -DNO_VIZ -D_LARGEFILE64_SOURCE=0 -D_FILE_OFFSET_BITS=64 -DCHDR_SYSTEM_ZLIB # For lib7z ifdef INCLUDE_7Z_SUPPORT diff --git a/makefile.sdl b/makefile.sdl index 4d33e76e7..3038ce2ea 100644 --- a/makefile.sdl +++ b/makefile.sdl @@ -53,7 +53,7 @@ alldir += burner burner/sdl burner/sdl dep/libs/libspng dep/libs/lib7z dep/libs intf/video/scalers intf/video/sdl intf/audio intf/audio/sdl intf/input intf/input/sdl intf/cd \ intf/perfcount intf/perfcount/sdl dep/generated -depobj += neocdlist.o \ +depobj += cdlist.o \ \ conc.o cong.o dat.o gamc.o gami.o image.o ioapi.o misc.o sshot.o state.o statec.o unzip.o zipfn.o \ \ diff --git a/makefile.sdl2 b/makefile.sdl2 index 0e9b6750c..1c32039a8 100644 --- a/makefile.sdl2 +++ b/makefile.sdl2 @@ -54,7 +54,7 @@ alldir += burner burner/sdl burner/sdl dep/libs/libspng dep/libs/lib7z dep/libs intf/video/scalers intf/video/sdl intf/audio intf/audio/sdl intf/input intf/input/sdl intf/cd \ intf/perfcount intf/perfcount/sdl dep/generated -depobj += neocdlist.o \ +depobj += cdlist.o \ \ conc.o cong.o dat.o gamc.o gami.o image.o ioapi.o misc.o sshot.o state.o statec.o unzip.o zipfn.o \ \ diff --git a/projectfiles/qtcreator/fba_qt.pro b/projectfiles/qtcreator/fba_qt.pro index c49de967f..b1e817f5a 100644 --- a/projectfiles/qtcreator/fba_qt.pro +++ b/projectfiles/qtcreator/fba_qt.pro @@ -84,13 +84,19 @@ INCLUDEPATH += \ $$SRC/dep/libs \ $$SRC/dep/libs/libpng \ $$SRC/dep/libs/zlib \ + $$SRC/dep/libs/libchdr \ + $$SRC/dep/libs/libchdr/include \ + $$SRC/dep/libs/lzma \ + $$SRC/dep/libs/lzma/include \ + $$SRC/dep/libs/zstd \ DEFINES += BUILD_QT \ LSB_FIRST \ "__fastcall=" \ "_fastcall=" \ WITH_QTCREATOR \ - INCLUDE_LIB_PNGH + INCLUDE_LIB_PNGH \ + CHDR_SYSTEM_ZLIB linux: DEFINES += BUILD_QT_LINUX macx: DEFINES += BUILD_QT_MACX @@ -389,7 +395,7 @@ $$DRV_NEOGEO { HEADERS += $$files(../../src/burn/drv/neogeo/*.h) SOURCES += $$files(../../src/burn/drv/neogeo/*.cpp) - SOURCES *= ../../src/burner/qt/neocdlist.cpp + SOURCES *= ../../src/burner/cdlist.cpp } #=============================================================================== @@ -659,6 +665,24 @@ SOURCES += \ ../../src/intf/audio/aud_interface.cpp \ ../../src/intf/audio/lowpass2.cpp \ ../../src/intf/cd/cd_interface.cpp \ + ../../src/intf/cd/cd_img.cpp \ + ../../src/intf/cd/cd_chd.cpp \ + ../../src/dep/libs/libchdr/libchdr_bitstream.c \ + ../../src/dep/libs/libchdr/libchdr_cdrom.c \ + ../../src/dep/libs/libchdr/libchdr_chd.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdfl.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdlz.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdzl.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdzs.c \ + ../../src/dep/libs/libchdr/libchdr_codec_flac.c \ + ../../src/dep/libs/libchdr/libchdr_codec_huff.c \ + ../../src/dep/libs/libchdr/libchdr_codec_lzma.c \ + ../../src/dep/libs/libchdr/libchdr_codec_zlib.c \ + ../../src/dep/libs/libchdr/libchdr_codec_zstd.c \ + ../../src/dep/libs/libchdr/libchdr_flac.c \ + ../../src/dep/libs/libchdr/libchdr_huffman.c \ + ../../src/dep/libs/lzma/libchdr_lzma.c \ + ../../src/dep/libs/zstd/zstddeclib.c \ ../../src/intf/input/inp_interface.cpp \ ../../src/intf/video/vid_interface.cpp \ ../../src/intf/video/vid_support.cpp \ diff --git a/projectfiles/qtcreator/fba_sdl.pro b/projectfiles/qtcreator/fba_sdl.pro index fd34e8a4a..aaad627d1 100644 --- a/projectfiles/qtcreator/fba_sdl.pro +++ b/projectfiles/qtcreator/fba_sdl.pro @@ -72,13 +72,19 @@ INCLUDEPATH += \ $$SRC/intf/cd \ $$SRC/dep/libs/libpng \ $$SRC/dep/libs/zlib \ + $$SRC/dep/libs/libchdr \ + $$SRC/dep/libs/libchdr/include \ + $$SRC/dep/libs/lzma \ + $$SRC/dep/libs/lzma/include \ + $$SRC/dep/libs/zstd \ DEFINES += BUILD_SDL \ LSB_FIRST \ "__fastcall=" \ "_fastcall=" \ WITH_QTCREATOR \ - INCLUDE_LIB_PNGH + INCLUDE_LIB_PNGH \ + CHDR_SYSTEM_ZLIB # no warnings... QMAKE_CXXFLAGS += -w @@ -1071,6 +1077,24 @@ SOURCES += \ ../../src/intf/audio/lowpass2.cpp \ ../../src/intf/audio/sdl/aud_sdl.cpp \ ../../src/intf/cd/cd_interface.cpp \ + ../../src/intf/cd/cd_img.cpp \ + ../../src/intf/cd/cd_chd.cpp \ + ../../src/dep/libs/libchdr/libchdr_bitstream.c \ + ../../src/dep/libs/libchdr/libchdr_cdrom.c \ + ../../src/dep/libs/libchdr/libchdr_chd.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdfl.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdlz.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdzl.c \ + ../../src/dep/libs/libchdr/libchdr_codec_cdzs.c \ + ../../src/dep/libs/libchdr/libchdr_codec_flac.c \ + ../../src/dep/libs/libchdr/libchdr_codec_huff.c \ + ../../src/dep/libs/libchdr/libchdr_codec_lzma.c \ + ../../src/dep/libs/libchdr/libchdr_codec_zlib.c \ + ../../src/dep/libs/libchdr/libchdr_codec_zstd.c \ + ../../src/dep/libs/libchdr/libchdr_flac.c \ + ../../src/dep/libs/libchdr/libchdr_huffman.c \ + ../../src/dep/libs/lzma/libchdr_lzma.c \ + ../../src/dep/libs/zstd/zstddeclib.c \ ../../src/intf/input/inp_interface.cpp \ ../../src/intf/input/sdl/inp_sdl.cpp \ ../../src/intf/video/vid_interface.cpp \ @@ -1078,7 +1102,7 @@ SOURCES += \ ../../src/intf/video/sdl/vid_sdlfx.cpp \ ../../src/intf/video/sdl/vid_sdlopengl.cpp \ ../../src/cpu/z80_intf.cpp \ - ../../src/burner/libretro/neocdlist.cpp \ + ../../src/burner/cdlist.cpp \ ../../src/intf/video/vid_softfx.cpp \ ../../src/intf/video/scalers/2xpm.cpp \ ../../src/intf/video/scalers/2xsai.cpp \ diff --git a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj index aa829a827..d6012c480 100644 --- a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj +++ b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj @@ -34,6 +34,7 @@ + @@ -66,7 +67,7 @@ - + @@ -75,7 +76,6 @@ - diff --git a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters index 677d44846..35fe8f2bd 100644 --- a/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters +++ b/projectfiles/visualstudio-2015/fbneo_vs2015.vcxproj.filters @@ -1465,8 +1465,8 @@ burner\win32 - - burner\win32 + + burner burner\win32 @@ -3388,6 +3388,9 @@ burner + + burner\win32 + burner\win32 @@ -3568,9 +3571,6 @@ burn\drv\pre90s - - burner\win32 - burn\drv\taito diff --git a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj index a3273d0eb..1c42008e5 100644 --- a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj +++ b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj @@ -34,6 +34,7 @@ + @@ -66,7 +67,7 @@ - + @@ -75,7 +76,6 @@ - diff --git a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters index 6dd0480ff..41e0ce6d3 100644 --- a/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters +++ b/projectfiles/visualstudio-2019/fbneo_vs2019.vcxproj.filters @@ -1465,8 +1465,8 @@ burner\win32 - - burner\win32 + + burner burner\win32 @@ -3388,6 +3388,9 @@ burner + + burner\win32 + burner\win32 @@ -3568,9 +3571,6 @@ burn\drv\pre90s - - burner\win32 - burn\drv\taito diff --git a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj index 0b4d45d05..4aa984fda 100644 --- a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj +++ b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj @@ -34,6 +34,7 @@ + @@ -66,7 +67,7 @@ - + @@ -75,7 +76,6 @@ - diff --git a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters index 0e7fc7c9f..33f7dfd26 100644 --- a/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters +++ b/projectfiles/visualstudio-2022/fbneo_vs2022.vcxproj.filters @@ -1465,8 +1465,8 @@ burner\win32 - - burner\win32 + + burner burner\win32 @@ -3388,6 +3388,9 @@ burner + + burner\win32 + burner\win32 @@ -3568,9 +3571,6 @@ burn\drv\pre90s - - burner\win32 - burn\drv\taito diff --git a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj index e19b34f62..5f85c89b0 100644 --- a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj +++ b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj @@ -1036,6 +1036,7 @@ + @@ -1069,7 +1070,7 @@ - + @@ -1078,7 +1079,6 @@ - diff --git a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters index f880f4c9b..f20867af1 100644 --- a/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters +++ b/projectfiles/visualstudio-2026/fbneo_vs2026.vcxproj.filters @@ -3007,6 +3007,9 @@ burner + + burner\win32 + burner @@ -3106,8 +3109,8 @@ burner\win32 - - burner\win32 + + burner burner\win32 @@ -3133,9 +3136,6 @@ burner\win32 - - burner\win32 - burner\win32 diff --git a/src/burner/cdlist.cpp b/src/burner/cdlist.cpp new file mode 100644 index 000000000..028d31a79 --- /dev/null +++ b/src/burner/cdlist.cpp @@ -0,0 +1,590 @@ +#include "burner.h" +#include "cdlist.h" +#include "neocdlist.h" +#include "cd_img.h" +#include "neocdlist_games.h" +#include "pcecdlist_games.h" + +static NGCDGAME* pNeoGame; + +static void CopyText(TCHAR* pszDest, UINT32 nDest, const TCHAR* pszSource) +{ + if (!pszDest || !nDest) + return; + + if (!pszSource) + pszSource = _T(""); + _tcsncpy(pszDest, pszSource, nDest - 1); + pszDest[nDest - 1] = 0; +} + +static TCHAR* DuplicateText(const TCHAR* pszSource) +{ + if (!pszSource) + return NULL; + + UINT32 nLength = (UINT32)_tcslen(pszSource) + 1; + TCHAR* pszDest = (TCHAR*)malloc(nLength * sizeof(TCHAR)); + if (pszDest) + memcpy(pszDest, pszSource, nLength * sizeof(TCHAR)); + return pszDest; +} + +static INT32 FindTextI(const TCHAR* pszText, const TCHAR* pszNeedle) +{ + if (!pszText || !pszNeedle || !*pszNeedle) + return 0; + + for (; *pszText; pszText++) { + const TCHAR* pText = pszText; + const TCHAR* pNeedle = pszNeedle; + while (*pText && *pNeedle && _totlower(*pText) == _totlower(*pNeedle)) { + pText++; pNeedle++; + } + if (!*pNeedle) + return 1; + } + return 0; +} + +static UINT32 ReadLE32(const UINT8* pData) +{ + return (UINT32)pData[0] | ((UINT32)pData[1] << 8) | ((UINT32)pData[2] << 16) | ((UINT32)pData[3] << 24); +} + +static INT32 ReadSector(CDImage* pImage, INT32 nLba, UINT8* pData, INT32* pnSize) +{ + INT32 nSize = 0; + if (CDImageReadUserSector(pImage, nLba, pData, &nSize)) + return 0; + + if (pnSize) + *pnSize = nSize; + return nSize > 0; +} + +static const CDImageTrack* FindDataTrack(const CDImage* pImage) +{ + for (INT32 i = 0; i < CDImageGetTrackCount(pImage); i++) { + const CDImageTrack* pTrack = CDImageGetTrack(pImage, i); + if (pTrack && pTrack->nType != CDIMAGE_TRACK_AUDIO) + return pTrack; + } + return NULL; +} + +NGCDGAME* GetNeoGeoCDInfo(UINT32 nID) +{ + for (UINT32 i = 0; i < sizeof(games) / sizeof(games[0]); i++) { + if (games[i].id == nID) + return &games[i]; + } + return NULL; +} + +void FreeNGCDGame(NGCDGAME** ppGame) +{ + if (!ppGame || !*ppGame) + return; + + free_s((void**)&((*ppGame)->pszName)); + free_s((void**)&((*ppGame)->pszTitle)); + free_s((void**)&((*ppGame)->pszYear)); + free_s((void**)&((*ppGame)->pszCompany)); + free(*ppGame); *ppGame = NULL; +} + +INT32 GetNGCDGameTitle(const UINT32 nGameID, NGCDGAME** ppOutGame, bool bPrintLog) +{ + if (!ppOutGame) + return 0; + + *ppOutGame = NULL; + NGCDGAME* pSource = GetNeoGeoCDInfo(nGameID); + if (!pSource) + return 0; + + NGCDGAME* pGame = (NGCDGAME*)calloc(1, sizeof(NGCDGAME)); + if (!pGame) + return 0; + + pGame->id = pSource->id; + pGame->pszName = DuplicateText(pSource->pszName); + pGame->pszTitle = DuplicateText(pSource->pszTitle); + pGame->pszYear = DuplicateText(pSource->pszYear); + pGame->pszCompany = DuplicateText(pSource->pszCompany); + if (!pGame->pszName || !pGame->pszTitle || !pGame->pszYear || !pGame->pszCompany) { + FreeNGCDGame(&pGame); + return 0; + } + if (bPrintLog) { + bprintf(PRINT_NORMAL, _T(" Title: %s\n" ), pGame->pszTitle); + bprintf(PRINT_NORMAL, _T(" Shortname: %s\n"), pGame->pszName); + bprintf(PRINT_NORMAL, _T(" Year: %s\n" ), pGame->pszYear); + bprintf(PRINT_NORMAL, _T(" Company: %s\n" ), pGame->pszCompany); + } + *ppOutGame = pGame; + return 1; +} + +static UINT32 ReviseNeoID(UINT32 nID, const UINT8* pDate, const UINT8* pData, INT32 bGotDDPRG, const char* pszVolume, const TCHAR* pszPath) +{ + if ((nID == 0x0016 || nID == 0x0025 || nID == 0x0076 || nID == 0x0062) && pDate[0] == 94 && pDate[1] == 12 && pDate[2] == 20) nID |= 0x1000; // Justin Gibbons Hacks (kotm | eightman |zedblade | spinmast) + if ( nID == 0x0059 && pDate[0] == 95 && pDate[1] == 6 && pDate[2] == 20) nID |= 0x1000; // Savage Reign Rev 1 + if ( nID == 0x0066 && pDate[0] == 125 && pDate[1] == 4 && pDate[2] == 10) nID |= 0x1200; // Digger Man (Prototype) + if ( nID == 0x069c && pDate[0] == 95 && pDate[1] == 4 && pDate[2] == 29) nID |= 0x1000; // Fatal Fury 3 Rev 1 + if ( nID == 0x069c && pDate[0] == 95 && pDate[1] == 6 && pDate[2] == 1) nID |= 0x2000; // Fatal Fury 3 Rev 2 + if ( nID == 0x069c && pDate[0] == 95 && pDate[1] == 7 && pDate[2] == 10) nID |= 0x3000; // Fatal Fury 3 Rev 3 + if ( nID == 0x0090 && pDate[0] == 95 && pDate[1] == 7 && pDate[2] == 21) nID |= 0x1000; // World Heroes Perfect + if ( nID == 0x0058 && pDate[0] == 94 && pDate[1] == 10 && pDate[2] == 14) nID |= 0x1000; // Fatal Fury Special Rev 1 + if ( nID == 0x0052 && pDate[0] == 123 && pDate[1] == 7 && pDate[2] == 1) nID |= 0x1000; // Abyssal Infants + if ( nID == 0x0052 && pDate[0] == 123 && pDate[1] == 6 && pDate[2] == 20) nID |= 0x1001; // Neo Fight + if ( nID == 0x0082) { + if (bGotDDPRG) nID |= 0x1000; // Double Dragon Rev 1 + else if (FindTextI(pszPath, _T("OST")) || FindTextI(pszPath, _T("PS")) || FindTextI(pszPath, _T("PlayStation"))) nID |= 0x2000; // Double Dragon PS1 OST + } + if (nID == 0x0085) { + if ( pDate[0] == 123 && pDate[1] == 11 && pDate[2] == 29) nID |= 0x1000; // Samurai Shodown RPG (English Translation) + else if (pDate[0] == 124 && pDate[1] == 1 && pDate[2] == 26) nID |= 0x3000; // Samurai Shodown RPG (English Translation v1.1) + else if (pDate[0] == 126 && pDate[1] == 6 && pDate[2] == 1) nID |= 0x4000; // Samurai Shodown RPG (Simplified Chinese Translation v1.1) + else if (FindTextI(pszPath, _T("FR")) || FindTextI(pszPath, _T("French"))) nID |= 0x2000; // Samurai Shodown RPG (FR) + } + if (nID == 0x5345 && (!strcmp(pszVolume, "BLUEANDREDFIGHTTHEROBOTS") || FindTextI(pszPath, _T("NTSC")))) nID = 0x5346; // Blue And Red - Fight The Robots! (NTSC) + if (nID == 0x1234 && pDate[0] == 105 && pDate[1] == 4 && pDate[2] == 25) nID = 0x2234; // Neo Puzzle League + if (nID == 0x1234 && pDate[0] == 124 && pDate[1] == 12 && pDate[2] == 2) nID = 0x2235; // Neo Tetris + if (nID == 0x1234 && pDate[0] == 112 && pDate[1] == 3 && pDate[2] == 4) nID = 0x2236; // NGD::ARK + if (nID == 0x1234 && pDate[0] == 112 && pDate[1] == 12 && pDate[2] == 4) nID = 0x2237; // Santa Ball + if (nID == 0x2000 && !strcmp(pszVolume, "COLUMNS")) nID |= 0x1000; // Columns + if (nID == 0xffff && !strcmp(pszVolume, "CODENAME BLUT ENGEL")) nID = 0xfffe; // Codename Blut Engel + if (nID == 0x7777 && pDate[0] == 114 && pDate[1] == 8 && pDate[2] == 14) nID = 0x7778; // Puzzle de Pon! CD Collection + if (nID == 0x2019 && !strcmp(pszVolume, "LOOPTRSP")) nID |= 0x0100; // Looptris Plus + if (nID == 0x0048 && pData[0x67] == 0x08) nID |= 0x1000; // Treasure of Caribbean (c) 1994 / (c) 2011 NCI + if (nID == 0x0055 && pData[0x67] == 0xdE) /* 10-6-1994 (P1.PRG) */ {/* ...continue*/ } // King of Fighters '94, The (1994)(SNK)(JP) + if (nID == 0x0055 && pData[0x67] == 0xe6) /* 11-21-1994 (P1.PRG) */ nID |= 0x1000; // King of Fighters '94, The (1994)(SNK)(JP-US) + if (nID == 0x0084 && pData[0x6C] == 0xc0) /* 9-11-1995 (P1.PRG) */ {/* ...continue*/ } // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B01, B03-B06, NGCD-084E MT B01] + if (nID == 0x0084 && pData[0x6c] == 0xff) /* 10-5-1995 (P1.PRG) */ nID |= 0x1000; // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B10, NGCD-084E MT B03] + return nID; +} + +static INT32 IdentifyNeo(CDImage* pImage, const TCHAR* pszPath, CDListResult* pResult) +{ + const CDImageTrack* pTrack = FindDataTrack(pImage); + if (!pTrack) + return 0; + + UINT8 pvd[2448] = { 0 }; + INT32 nSize = 0; + if (!ReadSector(pImage, pTrack->nIndex1LBA + 16, pvd, &nSize) || nSize < 190 || memcmp(pvd + 1, "CD001", 5)) + return 0; + + char szVolume[33] = { 0 }; + memcpy(szVolume, pvd + 40, 32); + for (INT32 i = 31; i >= 0 && szVolume[i] == ' '; i--) + szVolume[i] = 0; + UINT32 nRootLba = ReadLE32(pvd + 158); + UINT32 nRootSize = ReadLE32(pvd + 166); + if (!nRootLba || !nRootSize) + return 0; + + INT32 bGotDDPRG = 0; + INT32 bQueued = 0; + UINT32 nQueuedID = 0; + for (UINT32 nPosition = 0; nPosition < nRootSize;) { + UINT8 sector[2448] = { 0 }; + INT32 nSectorSize = 0; + UINT32 nSector = nPosition / 2048; + UINT32 nOffset = nPosition % 2048; + if (!ReadSector(pImage, pTrack->nIndex1LBA + (INT32)nRootLba + (INT32)nSector, sector, &nSectorSize)) break; + if (nOffset >= (UINT32)nSectorSize) + break; + UINT8 nLength = sector[nOffset]; + if (!nLength) { + nPosition = (nPosition + 2048) & ~2047U; + continue; + } + if (nOffset + nLength > (UINT32)nSectorSize || nLength < 34) + break; + const UINT8* pRecord = sector + nOffset; + UINT8 nNameLength = pRecord[32]; + if (!(pRecord[25] & 2) && nNameLength && 33 + nNameLength <= nLength) { + char szName[256] = { 0 }; + UINT32 nCopy = nNameLength < sizeof(szName) - 1 ? nNameLength : sizeof(szName) - 1; + memcpy(szName, pRecord + 33, nCopy); + char* pVersion = strchr(szName, ';'); + if (pVersion) + *pVersion = 0; + if (!strcmp(szName, "DDPRG.ACM")) + bGotDDPRG = 1; + UINT32 nFileLba = ReadLE32(pRecord + 2); + UINT8 data[2448] = { 0 }; + INT32 nDataSize = 0; + if (ReadSector(pImage, pTrack->nIndex1LBA + (INT32)nFileLba, data, &nDataSize) && nDataSize >= 0x10a && !memcmp(data + 0x100, "NEO-GEO", 7)) { + UINT32 nID = ((UINT32)data[0x108] << 8) | data[0x109]; + nID = ReviseNeoID(nID, pRecord + 18, data, bGotDDPRG, szVolume, pszPath); + if (nID == 0x0229) { + pResult->nNeoID = nID; + return 1; + } + if ( nID == 0x0214 || // King of Fighters '96, The + (nID == 0x0058 && !(pRecord[18] == 94 && pRecord[19] == 8 && pRecord[20] == 5))) { // !Fatal Fury Special Rev 0 + // continue checking other files... + bQueued = 1; + nQueuedID = nID; + } else { + pResult->nNeoID = nID; + return 1; + } + } + } + nPosition += nLength; + } + if (bQueued) { + pResult->nNeoID = nQueuedID; + return 1; + } + return 0; +} + +struct CDListSha1 +{ + UINT32 State[5]; + UINT64 nBytes; + UINT8 Buffer[64]; +}; + +static UINT32 RotateLeft(UINT32 nValue, INT32 nBits) +{ + return (nValue << nBits) | (nValue >> (32 - nBits)); +} + +static void Sha1Transform(CDListSha1* pSha1, const UINT8* pData) +{ + UINT32 w[80]; + for (INT32 i = 0; i < 16; i++) + w[i] = ((UINT32)pData[i * 4] << 24) | ((UINT32)pData[i * 4 + 1] << 16) | ((UINT32)pData[i * 4 + 2] << 8) | pData[i * 4 + 3]; + for (INT32 i = 16; i < 80; i++) + w[i] = RotateLeft(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); + + UINT32 a = pSha1->State[0]; + UINT32 b = pSha1->State[1]; + UINT32 c = pSha1->State[2]; + UINT32 d = pSha1->State[3]; + UINT32 e = pSha1->State[4]; + for (INT32 i = 0; i < 80; i++) { + UINT32 f, k; + if (i < 20) { + f = (b & c) | (~b & d); k = 0x5a827999; + } else if (i < 40) { + f = b ^ c ^ d; k = 0x6ed9eba1; + } else if (i < 60) { + f = (b & c) | (b & d) | (c & d); k = 0x8f1bbcdc; + } else { + f = b ^ c ^ d; k = 0xca62c1d6; + } + UINT32 nTemp = RotateLeft(a, 5) + f + e + k + w[i]; + e = d; + d = c; + c = RotateLeft(b, 30); + b = a; + a = nTemp; + } + pSha1->State[0] += a; + pSha1->State[1] += b; + pSha1->State[2] += c; + pSha1->State[3] += d; + pSha1->State[4] += e; +} + +static void Sha1Init(CDListSha1* pSha1) +{ + pSha1->State[0] = 0x67452301; + pSha1->State[1] = 0xefcdab89; + pSha1->State[2] = 0x98badcfe; + pSha1->State[3] = 0x10325476; + pSha1->State[4] = 0xc3d2e1f0; + pSha1->nBytes = 0; +} + +static void Sha1Update(CDListSha1* pSha1, const UINT8* pData, UINT32 nLength) +{ + UINT32 nUsed = (UINT32)(pSha1->nBytes & 63); + pSha1->nBytes += nLength; + while (nLength) { + UINT32 nCopy = 64 - nUsed; + if (nCopy > nLength) + nCopy = nLength; + memcpy(pSha1->Buffer + nUsed, pData, nCopy); + nUsed += nCopy; + pData += nCopy; + nLength -= nCopy; + if (nUsed == 64) { + Sha1Transform(pSha1, pSha1->Buffer); + nUsed = 0; + } + } +} + +static void Sha1Final(CDListSha1* pSha1, UINT8* pDigest) +{ + UINT64 nBits = pSha1->nBytes * 8; + UINT8 pFinal[72] = { 0x80 }; + UINT32 nUsed = (UINT32)(pSha1->nBytes & 63); + UINT32 nPadding = nUsed < 56 ? 56 - nUsed : 120 - nUsed; + for (INT32 i = 0; i < 8; i++) + pFinal[nPadding + i] = (UINT8)(nBits >> (56 - i * 8)); + + Sha1Update(pSha1, pFinal, nPadding + 8); + for (INT32 i = 0; i < 5; i++) { + pDigest[i * 4 + 0] = (UINT8)(pSha1->State[i] >> 24); + pDigest[i * 4 + 1] = (UINT8)(pSha1->State[i] >> 16); + pDigest[i * 4 + 2] = (UINT8)(pSha1->State[i] >> 8); + pDigest[i * 4 + 3] = (UINT8) pSha1->State[i]; + } +} + +static INT32 HashDataTrack(CDImage* pImage, const CDImageTrack* pTrack, UINT8* pDigest) +{ + if (!pImage || !pTrack || !pDigest || pTrack->nSectors <= 0) + return 0; + + CDListSha1 Sha1; + Sha1Init(&Sha1); + UINT8 sector[2448]; + for (INT32 i = 0; i < pTrack->nSectors; i++) { + INT32 nSize = 0; + if (!ReadSector(pImage, pTrack->nIndex1LBA + i, sector, &nSize)) + return 0; + Sha1Update(&Sha1, sector, (UINT32)nSize); + } + Sha1Final(&Sha1, pDigest); + return 1; +} + +static UINT32 CalculateCrc32(const UINT8* pData, UINT32 nLength) +{ + UINT32 nCrc = 0xffffffff; + for (UINT32 i = 0; i < nLength; i++) { + nCrc ^= pData[i]; + for (INT32 j = 0; j < 8; j++) + nCrc = (nCrc >> 1) ^ (0xedb88320U & (0U - (nCrc & 1))); + } + return nCrc ^ 0xffffffff; +} + +static INT32 IsGamesExpress(CDImage* pImage, const CDImageTrack* pTrack, UINT8* pSector) +{ + INT32 nSize = 0; + if (!ReadSector(pImage, pTrack->nIndex1LBA + 0x10, pSector, &nSize) || nSize < 0x1c) return 0; + if (!memcmp(pSector + 8, "HACKER CD ROM SYSTEM", 20)) return 1; + if (memcmp(pSector + 1, "CD001", 5)) return 0; + if (!ReadSector(pImage, pTrack->nIndex1LBA + 0x14, pSector, &nSize) || nSize != 2048) return 0; + UINT32 nCrc = CalculateCrc32(pSector, 2048); + return nCrc == 0xd7b47c06 || nCrc == 0x86aec522 || nCrc == 0xc8d1b5ef || nCrc == 0x0bdbde64; +} + +static INT32 IdentifyPlatform(CDImage* pImage, CDListResult* pResult) +{ + static const UINT8 pPceMagic[32] = { + 0x82, 0xb1, 0x82, 0xcc, 0x83, 0x76, 0x83, 0x8d, + 0x83, 0x4f, 0x83, 0x89, 0x83, 0x80, 0x82, 0xcc, + 0x92, 0x98, 0x8d, 0xec, 0x8c, 0xa0, 0x82, 0xcd, + 0x8a, 0x94, 0x8e, 0xae, 0x89, 0xef, 0x8e, 0xd0 + }; + UINT8 sector[2448] = { 0 }; + for (INT32 i = 0; i < CDImageGetTrackCount(pImage); i++) { + const CDImageTrack* pTrack = CDImageGetTrack(pImage, i); + INT32 nSize = 0; + if (pTrack && pTrack->nType != CDIMAGE_TRACK_AUDIO && ReadSector(pImage, pTrack->nIndex1LBA, sector, &nSize) && nSize >= 15 && !memcmp(sector, "PC-FX:Hu_CD-ROM", 15)) { + pResult->nPlatform = CDLIST_PLATFORM_PCFX; + pResult->nSource = CDLIST_SOURCE_MAGIC; + pResult->nConfidence = CDLIST_CONFIDENCE_EXACT; + CopyText(pResult->Metadata.szDescriptor, CDLIST_TEXT_SIZE, _T("PC-FX boot signature")); + return 1; + } + } + const CDImageTrack* pTrack = FindDataTrack(pImage); + if (!pTrack) + return 0; + + INT32 nSize = 0; + if (ReadSector(pImage, pTrack->nIndex1LBA, sector, &nSize) && nSize >= 32 && !memcmp(sector, pPceMagic, sizeof(pPceMagic))) { + pResult->nPlatform = CDLIST_PLATFORM_PCECD; + pResult->nSource = CDLIST_SOURCE_MAGIC; + pResult->nConfidence = CDLIST_CONFIDENCE_STRONG; + CopyText(pResult->Metadata.szRequirement, CDLIST_TEXT_SIZE, _T("PC Engine CD System Card")); + CopyText(pResult->Metadata.szDescriptor, CDLIST_TEXT_SIZE, _T("PC Engine program copyright header")); + return 1; + } + if (ReadSector(pImage, pTrack->nIndex1LBA + 1, sector, &nSize) && nSize >= 55 && !memcmp(sector + 0x20, "PC Engine CD-ROM SYSTEM", 23)) { + pResult->nPlatform = CDLIST_PLATFORM_PCECD; + pResult->nSource = CDLIST_SOURCE_MAGIC; + pResult->nConfidence = CDLIST_CONFIDENCE_EXACT; + CopyText(pResult->Metadata.szRequirement, CDLIST_TEXT_SIZE, _T("PC Engine CD System Card")); + CopyText(pResult->Metadata.szDescriptor, CDLIST_TEXT_SIZE, _T("PC Engine CD-ROM SYSTEM header")); + return 1; + } + if (IsGamesExpress(pImage, pTrack, sector)) { + pResult->nPlatform = CDLIST_PLATFORM_PCECD; + pResult->nSource = CDLIST_SOURCE_MAGIC; + pResult->nConfidence = CDLIST_CONFIDENCE_EXACT; + CopyText(pResult->Metadata.szRequirement, CDLIST_TEXT_SIZE, _T("Games Express CD Card")); + CopyText(pResult->Metadata.szDescriptor, CDLIST_TEXT_SIZE, _T("Games Express disc signature")); + return 1; + } + return 0; +} + +#ifdef BUILD_WIN32 +static void CopyAnsiText(TCHAR* pszDest, UINT32 nDest, const char* pszSource) +{ + if (!pszDest || !nDest) return; + INT32 nLength = pszSource ? MultiByteToWideChar(65001, 0, pszSource, -1, NULL, 0) : 0; + if (nLength > 0) MultiByteToWideChar(65001, 0, pszSource, -1, pszDest, (int)nDest); + if (nLength <= 0) pszDest[0] = 0; + pszDest[nDest - 1] = 0; +} +#else +#define CopyAnsiText CopyText +#endif + +static INT32 FindPceChdGame(const UINT8* pSha1, CDListMetadata* pMetadata) +{ + if (!pSha1 || !pMetadata) + return 0; + + UINT32 nLow = 0; + UINT32 nHigh = pceCdGameCount; + while (nLow < nHigh) { + UINT32 nMiddle = nLow + (nHigh - nLow) / 2; + INT32 nCompare = memcmp(pSha1, pceCdGames[nMiddle].sha1, CDLIST_SHA1_SIZE); + if (nCompare > 0) nLow = nMiddle + 1; + else nHigh = nMiddle; + } + if (nLow >= pceCdGameCount || memcmp(pSha1, pceCdGames[nLow].sha1, CDLIST_SHA1_SIZE)) return 0; + const PceCdGame* pGame = &pceCdGames[nLow]; + CopyAnsiText(pMetadata->szName, CDLIST_TEXT_SIZE, pceCdGameStringData + pGame->shortname); + CopyAnsiText(pMetadata->szTitle, CDLIST_TEXT_SIZE, pceCdGameStringData + pGame->description); + CopyAnsiText(pMetadata->szYear, 32, pceCdGameStringData + pGame->year); + CopyAnsiText(pMetadata->szCompany, CDLIST_TEXT_SIZE, pceCdGameStringData + pGame->publisher); + CopyAnsiText(pMetadata->szRegion, 64, pceCdGameStringData + pGame->region); + CopyAnsiText(pMetadata->szRequirement, CDLIST_TEXT_SIZE, pceCdGameStringData + pGame->requirement); + CopyText(pMetadata->szDescriptor, CDLIST_TEXT_SIZE, _T("MAME pcecd CHD SHA1")); + return 1; +} + +static void FillNeoMetadata(CDListResult* pResult) +{ + NGCDGAME* pGame = GetNeoGeoCDInfo(pResult->nNeoID); + if (!pGame) + return; + + CopyText(pResult->Metadata.szName, CDLIST_TEXT_SIZE, pGame->pszName); + CopyText(pResult->Metadata.szTitle, CDLIST_TEXT_SIZE, pGame->pszTitle); + CopyText(pResult->Metadata.szYear, 32, pGame->pszYear); + CopyText(pResult->Metadata.szCompany, CDLIST_TEXT_SIZE, pGame->pszCompany); + CopyText(pResult->Metadata.szDescriptor, CDLIST_TEXT_SIZE, _T("Neo Geo CD program header")); +} + +INT32 CDListIdentify(const TCHAR* pszPath, CDListResult* pResult) +{ + if (!pszPath || !pResult) + return 0; + + memset(pResult, 0, sizeof(*pResult)); + CDImage* pImage = CDImageOpen(pszPath); + if (!pImage) + return 0; + + if (!CDImageGetChdSha1(pImage, pResult->ChdSha1)) { + pResult->bHasChdSha1 = 1; + pResult->bRawSha1Unsupported = 1; + pResult->bChdMameMatch = FindPceChdGame(pResult->ChdSha1, &pResult->ChdMetadata); + } else { + const CDImageTrack* pTrack = FindDataTrack(pImage); + pResult->bHasRawSha1 = HashDataTrack(pImage, pTrack, pResult->RawSha1); + } + if (IdentifyNeo(pImage, pszPath, pResult)) { + pResult->nPlatform = CDLIST_PLATFORM_NEOCD; + pResult->nSource = CDLIST_SOURCE_NEO_ID; + pResult->nConfidence = CDLIST_CONFIDENCE_EXACT; + FillNeoMetadata(pResult); + } else { + IdentifyPlatform(pImage, pResult); + if (pResult->bChdMameMatch) { + pResult->Metadata = pResult->ChdMetadata; + pResult->nPlatform = CDLIST_PLATFORM_PCECD; + pResult->nSource = CDLIST_SOURCE_CHD_SHA1; + pResult->nConfidence = CDLIST_CONFIDENCE_EXACT; + } + } + CDImageClose(pImage); + return pResult->nPlatform != CDLIST_PLATFORM_UNKNOWN; +} + +INT32 NeoCDList_CheckISO(TCHAR* pszFile, void (*pfEntryCallBack)(INT32, TCHAR*)) +{ + CDListResult Result; + if (CDListIdentify(pszFile, &Result) && Result.nPlatform == CDLIST_PLATFORM_NEOCD) { + if (pfEntryCallBack) pfEntryCallBack((INT32)Result.nNeoID, pszFile); + return 1; + } + return 0; +} + +INT32 GetNeoCDTitle(UINT32 nGameID) +{ + FreeNGCDGame(&pNeoGame); + return GetNGCDGameTitle(nGameID, &pNeoGame, true); +} + +INT32 GetNeoGeoCD_Identifier() +{ +#ifdef BUILD_NEOGEO + TCHAR* pszPath = GetIsoPath(); + if (!pszPath || !IsNeoGeoCD()) + return 0; + + CDListResult Result; + if (CDListIdentify(pszPath, &Result) && Result.nPlatform == CDLIST_PLATFORM_NEOCD) { + GetNeoCDTitle(Result.nNeoID); + return 1; + } + return 0; +#else + return 0; +#endif +} + +#ifdef BUILD_NEOGEO +INT32 NeoCDInfo_Init() +{ + NeoCDInfo_Exit(); + return GetNeoGeoCD_Identifier(); +} + +TCHAR* NeoCDInfo_Text(INT32 nText) +{ + if (!pNeoGame || !IsNeoGeoCD()) + return NULL; + + switch (nText) { + case DRV_NAME: return pNeoGame->pszName; + case DRV_FULLNAME: return pNeoGame->pszTitle; + case DRV_MANUFACTURER: return pNeoGame->pszCompany; + case DRV_DATE: return pNeoGame->pszYear; + } + return NULL; +} + +INT32 NeoCDInfo_ID() +{ + if (!pNeoGame || !IsNeoGeoCD()) + return 0; + return (INT32)pNeoGame->id; +} + +void NeoCDInfo_Exit() +{ + FreeNGCDGame(&pNeoGame); +} +#endif diff --git a/src/burner/cdlist.h b/src/burner/cdlist.h new file mode 100644 index 000000000..eec250792 --- /dev/null +++ b/src/burner/cdlist.h @@ -0,0 +1,56 @@ +#ifndef CDLIST_H_ +#define CDLIST_H_ + +#define CDLIST_SHA1_SIZE (20) +#define CDLIST_TEXT_SIZE (256) + +enum CDListPlatform { + CDLIST_PLATFORM_UNKNOWN = 0, + CDLIST_PLATFORM_NEOCD, + CDLIST_PLATFORM_PCECD, + CDLIST_PLATFORM_PCFX +}; + +enum CDListSource { + CDLIST_SOURCE_NONE = 0, + CDLIST_SOURCE_MAGIC, + CDLIST_SOURCE_NEO_ID, + CDLIST_SOURCE_CHD_SHA1, + CDLIST_SOURCE_RAW_SHA1 +}; + +enum CDListConfidence { + CDLIST_CONFIDENCE_NONE = 0, + CDLIST_CONFIDENCE_POSSIBLE, + CDLIST_CONFIDENCE_STRONG, + CDLIST_CONFIDENCE_EXACT +}; + +struct CDListMetadata { + TCHAR szName[CDLIST_TEXT_SIZE]; + TCHAR szTitle[CDLIST_TEXT_SIZE]; + TCHAR szYear[32]; + TCHAR szCompany[CDLIST_TEXT_SIZE]; + TCHAR szRegion[64]; + TCHAR szRequirement[CDLIST_TEXT_SIZE]; + TCHAR szDescriptor[CDLIST_TEXT_SIZE]; +}; + +struct CDListResult { + INT32 nPlatform; + INT32 nSource; + INT32 nConfidence; + UINT32 nNeoID; + CDListMetadata Metadata; + INT32 bHasChdSha1; + UINT8 ChdSha1[CDLIST_SHA1_SIZE]; + INT32 bChdMameMatch; + CDListMetadata ChdMetadata; + INT32 bHasRawSha1; + INT32 bRawSha1Unsupported; + UINT8 RawSha1[CDLIST_SHA1_SIZE]; +}; + +INT32 CDListIdentify(const TCHAR* pszPath, CDListResult* pResult); + +#endif diff --git a/src/burner/neocdlist.h b/src/burner/neocdlist.h index efcb8d924..84dadb21a 100644 --- a/src/burner/neocdlist.h +++ b/src/burner/neocdlist.h @@ -1,5 +1,7 @@ -#ifndef _neocdlist_ -#define _neocdlist_ +#ifndef NEOCDLIST_H_ +#define NEOCDLIST_H_ + +#include "cdlist.h" struct NGCDGAME { @@ -11,143 +13,28 @@ struct NGCDGAME }; NGCDGAME* GetNeoGeoCDInfo(UINT32 nID); -INT32 GetNeoCDTitle(UINT32 nGameID); -INT32 GetNeoGeoCD_Identifier(); +INT32 GetNGCDGameTitle(const UINT32 nGameID, NGCDGAME** ppOutGame, bool bPrintLog); +void FreeNGCDGame(NGCDGAME** ppGame); +INT32 NeoCDList_CheckISO(TCHAR* pszFile, void (*pfEntryCallBack)(INT32, TCHAR*)); +INT32 GetNeoCDTitle(UINT32 nGameID); +INT32 GetNeoGeoCD_Identifier(); #ifdef BUILD_NEOGEO bool IsNeoGeoCD(); // neo_run.cpp TCHAR* GetIsoPath(); // cd_isowav.cpp - INT32 NeoCDInfo_Init(); TCHAR* NeoCDInfo_Text(INT32 nText); INT32 NeoCDInfo_ID(); void NeoCDInfo_SetTitle(); void NeoCDInfo_Exit(); #else -static inline bool IsNeoGeoCD() { return false; } -static inline TCHAR* GetIsoPath() { return NULL; } -static inline int NeoCDInfo_Init() { return 0; } -static inline TCHAR* NeoCDInfo_Text(int) { return NULL; } -static inline int NeoCDInfo_ID() { return 0; } -static inline void NeoCDInfo_SetTitle() { } -static inline void NeoCDInfo_Exit() { } +static inline bool IsNeoGeoCD() { return false; } +static inline TCHAR* GetIsoPath() { return NULL; } +static inline INT32 NeoCDInfo_Init() { return 0; } +static inline TCHAR* NeoCDInfo_Text(INT32) { return NULL; } +static inline INT32 NeoCDInfo_ID() { return 0; } +static inline void NeoCDInfo_SetTitle() { } +static inline void NeoCDInfo_Exit() { } #endif -// ------------------------------------------------------ -// ISO9660 STUFF - -// ISO9660 date and time -struct iso9660_date -{ - UINT8 Year; // Number of year since 1900 - UINT8 Month; // Month (1 to 12) - UINT8 Day; // Day (1 to 31) - UINT8 Hour; // Hour (0 to 23) - UINT8 Minute; // Minute (0 to 59) - UINT8 Second; // Second (0 to 59) - UINT8 Zone; // Offset related to GMT, by 15-min INT32erval (from -48 to +52) -}; - -// ISO9660 BCD date and time -struct iso9660_bcd_date -{ - UINT8 Year[4]; - UINT8 Month[2]; - UINT8 Day[2]; - UINT8 Hour[2]; - UINT8 Minute[2]; - UINT8 Second[2]; - UINT8 SecFrac[2]; - char Zone; -}; - -struct iso9660_DirectoryRecord -{ - UINT8 len_dr; // [1] Length of Directory Record (bytes) - UINT8 ext_attr_rec_len; // [1] Extended Attribute Record Length (bytes) - UINT8 location_of_extent[8]; // [8] [LEF / BEF] LBN / Sector location of the file data - UINT8 data_lenth[8]; // [8] [LEF / BEF] Length of the file section (bytes) - iso9660_date rec_date_time; // [7] Recording Date and Time - UINT8 file_flags; // [1] 8-bit flags - // [bit 0] File is Hidden if this bit is 1 - // [bit 1] Entry is a Directory if this bit is 1 - // [bit 2] Entry is an Associated file is this bit is 1 - // [bit 3] Information is structured according to the extended attribute record if this bit is 1 - // [bit 4] Owner, group and permissions are specified in the extended attribute record if this bit is 1 - // [bit 5] Reserved (0) - // [bit 6] Reserved (0) - // [bit 7] File has more than one directory record if this bit is 1 - UINT8 file_unit_size; // [1] This field is only valid if the file is recorded in INT32erleave mode, otherwise this field is (00) - UINT8 INT32_gap_size; // [1] This field is only valid if the file is recorded in INT32erleave mode, otherwise this field is (00) - UINT8 vol_seq_number[4]; // [4] The ordinal number of the volume in the Volume Set on which the file described by the directory record is recorded. - UINT8 len_fi; // [1] Length of File Identifier (LEN_FI) - char* file_id; // [LEN_FI] File Identifier -}; - -struct iso9660_PathTableRecord -{ - UINT8 len_di; // [1] Length of Directory Identifier - UINT8 extended_attr_len; // [1] Extended Attribute Record Length - UINT8 extent_location[4]; // [4] Extent Location (Sector) - UINT8 parent_dir_num; // [1] Parent Directory Number - char* directory_id; // [LEN_DI] Directory Identifier -}; - -// Volume Descriptor Header (Sector 16) -struct iso9660_VDH -{ - char vdtype; // [1] Volume Descriptor Type - UINT8 stdid[5]; // [5] ISO9660 Standard Identifier - char vdver; // [1] Volume Descriptor Version -}; - -// Primary Volume Descriptor (Volume Descriptor Type 1) -struct iso9660_PVD -{ - // ----------------------------- - // Volume Descriptor Header - char type; - UINT8 id[5]; - char version; - // ----------------------------- - // Primary Volume Descriptor - char unused1; - UINT8 system_id[32]; - UINT8 volume_id[32]; - UINT8 unused2[8]; - UINT8 volume_space_size[8]; - UINT8 unused3[32]; - UINT8 volume_set_size[4]; - UINT8 volume_sequence_number[4]; - UINT8 logical_block_size[4]; - UINT8 path_table_size[8]; - UINT8 type_l_path_table[4]; - UINT8 opt_type_l_path_table[4]; - UINT8 type_m_path_table[4]; - UINT8 opt_type_m_path_table[4]; - iso9660_DirectoryRecord root_directory_record; - UINT8 volume_set_id[128]; - UINT8 publisher_id[128]; - UINT8 preparer_id[128]; - UINT8 application_id[128]; - UINT8 copyright_file_id[37]; - UINT8 abstract_file_id[37]; - UINT8 bibliographic_file_id[37]; - - iso9660_bcd_date creation_date; // [17] - iso9660_bcd_date modification_date; // [17] - iso9660_bcd_date expiration_date; // [17] - iso9660_bcd_date effective_date; // [17] - - char file_structure_version; - char unused4; - UINT8 application_data[512]; - UINT8 unused5[643]; -}; - -void iso9660_ReadOffset(UINT8 *Dest, FILE* fp, UINT32 lOffset, UINT32 lSize, UINT32 lLength); - -// ------------------------------------------------------ - - #endif diff --git a/src/burner/pcecdlist_games.h b/src/burner/pcecdlist_games.h new file mode 100644 index 000000000..2304a8c29 --- /dev/null +++ b/src/burner/pcecdlist_games.h @@ -0,0 +1,1655 @@ +// Generated by tools/cdlist/generate_pce_cd_db.py; do not edit. +// Source: https://raw.githubusercontent.com/mamedev/mame/ab41620cf2d4bace0497922b03cb48e1d5169b50/hash/pcecd.xml +// MAME: ab41620cf2d4bace0497922b03cb48e1d5169b50 (2026-08-12); input SHA1: 2da275959c6ca57c74e7c3f633bfd0267cda5687; license: CC0-1.0. + +#ifndef FBNEO_PCECD_GAMES_H +#define FBNEO_PCECD_GAMES_H + +enum { PCECD_SUPPORTED = 0, PCECD_PARTIAL = 1, PCECD_UNSUPPORTED = 2, PCECD_BADDUMP = 4 }; + +struct PceCdGame { + UINT8 sha1[20]; + UINT32 shortname, description, year, publisher, region, cloneof, requirement; + UINT8 flags; +}; + +static const char pceCdGameStringData[22328] = + "\0" + "1552 Tenka Tairan (Japan)\0" + "1552tenk\0" + "1988\0" + "1989\0" + "1990\0" + "1991\0" + "1991?\0" + "1992\0" + "1993\0" + "1994\0" + "1995\0" + "1996\0" + "1997\0" + "1999\0" + "199?\0" + "19??\0" + "2002\0" + "2004\0" + "2009\0" + "2011\0" + "2012\0" + "2013\0" + "2014\0" + "2016\0" + "2018\0" + "2021\0" + "3 in 1 DUO Demo CD (USA)\0" + "3in1\0" + "3x3 Eyes - Sanjiyan Hensei (ACD, SCD)(Japan)\0" + "3x3eyes\0" + "4 in 1 Super CD (USA)\0" + "4in1\0" + "\0" + "A. III (Japan)\0" + "ASK Kodansha\0" + "AV Tanjou (GECD)(Japan)\0" + "Accolade\0" + "Advanced Variable Geo (Japan)\0" + "Adventure Quiz - Capcom World Hatena no Daibouken (Japan)\0" + "Aetherbyte Studios\0" + "Ai Chou Aniki (Japan)\0" + "Akumajou Dracula X - Chi no Rondo (Japan)\0" + "Alnam no Kiba - Juuzoku Juuni Shinto Densetsu (Japan)\0" + "Alshark (Japan)\0" + "Ane-San (Japan)\0" + "Angelus 2 - Holy Night (Japan, prototype)\0" + "Aoki Ookami to Shiroki Mejika - Genchou Hishi (Japan)\0" + "Artdink\0" + "Asia Soft Lab\0" + "Asuka 120% Maxima Burning Fest (Japan)\0" + "Atlus\0" + "Aurora Quest - Otaku no Seiza (Japan)\0" + "Avenger (Japan)\0" + "Babel (Japan)\0" + "Baby Jo (Japan)\0" + "Bakuden Unbalance Zone (Japan)\0" + "Bakuretsu Hunter (Japan)\0" + "Bakushou Yoshimoto no Shinkigeki (Japan)\0" + "Bandai\0" + "Banpresto\0" + "Basted (Japan)\0" + "Bazaru Degozaru no Game Degozaru (Japan)\0" + "Beyond Shadowgate (USA)\0" + "Bikini Girls (USA)\0" + "Bikkuriman Daijikai (Japan)\0" + "Bishoujo Janshi Idol Pai (GECD)(Japan)\0" + "Bishoujo Senshi Sailor Moon (Japan)\0" + "Bishoujo Senshi Sailor Moon Collection (Japan)\0" + "Black Hole Assault (Japan)\0" + "Blood Gear (Japan)\0" + "Bold Game Studio\0" + "Bomberman '94 Taikenban (Japan)\0" + "Bomberman - Panic Bomber (Japan)\0" + "Bonanza Bros. (Japan)\0" + "Bonk III - Bonk's Big Adventure (USA)\0" + "Brain Gray\0" + "Brandish (ACD, SCD)(Japan)\0" + "Browning (Japan)\0" + "Br\303\270derbund\0" + "Builderland (Japan)\0" + "Burai - Hachigyoku no Yuushi Densetsu (Japan)\0" + "Burai II - Yami Koutei no Gyakushuu (Japan)\0" + "Buster Bros. (USA)\0" + "CD Battle Hikari no Yuushatachi (Japan)\0" + "CD Bishoujo Pachinko Kyuuma Yon Shimai (Japan)\0" + "CD Denjin Rockabilly Tengoku (Japan)\0" + "CD Hanafuda Bishoujo Fan Club (GECD)(Japan)\0" + "CD Mahjong Bishoujo Chuushinha (GECD)(Japan)\0" + "CD Pachisuro Bishoujo Gambler (GECD)(Japan)\0" + "Cal II (Japan)\0" + "Cal III (Japan)\0" + "Camp California (USA)\0" + "Capcom\0" + "Cardangels (Japan)\0" + "Championship Rally (Japan)\0" + "Chiki Chiki Boys (Japan)\0" + "Chou Aniki (Japan)\0" + "Chou Eiyuu Densetsu - Dynastic Hero (Japan)\0" + "Choujikuu Yousai Macross 2036 (CD, SCD)(Japan)\0" + "Choujikuu Yousai Macross Eien no Love Song (Japan)\0" + "Cinemaware\0" + "Cobra - Kokuryuuou no Densetsu (Japan)\0" + "Cobra II - Densetsu no Otoko (Japan)\0" + "Coconuts Japan\0" + "Color Wars (Japan)\0" + "Cosmic Fantasy - Bouken Shounen Yuu (Japan)\0" + "Cosmic Fantasy II (USA)\0" + "Cosmic Fantasy II - Bouken Shounen Ban (Japan)\0" + "Cosmic Fantasy III - Bouken Shounen Rei (Japan)\0" + "Cosmic Fantasy IV - Ginga Shounen Densetsu Gekitou Hen (Japan)\0" + "Cosmic Fantasy IV - Ginga Shounen Densetsu Totsunyuu Hen (Japan)\0" + "Cotton - Fantastic Night Dreams (Japan)\0" + "Cotton - Fantastic Night Dreams (USA)\0" + "Crazy Hospital - Fushigi no Kuni no Tenshi (Japan)\0" + "Crest of Wolf (Japan)\0" + "Cyber City OEDO 808 (Japan)\0" + "Daisenpuu Custom (Japan)\0" + "Daisenryaku II - Campaign Version (Japan)\0" + "Data West\0" + "Dead of the Brain I II (Japan)\0" + "Death Bringer - The Knight of Darkness (Japan)\0" + "Deden no Den (Japan)\0" + "Dekoboko Densetsu - Hashire Wagamanma (Japan)\0" + "Dengeki\0" + "Dennou Tenshi - Digital Angel (Japan)\0" + "Develo Magazine Volume I (Japan)\0" + "Develo Starter Kit - Assembler (Japan)\0" + "Develo Starter Kit - Basic (Japan)\0" + "De\302\267Ja (Japan)\0" + "Doraemon Nobita no Dorabian Night (Japan)\0" + "Double Dragon II - The Revenge (Japan)\0" + "Doukyuusei (Japan)\0" + "Download II (Japan)\0" + "Downtown Nekketsu Monogatari (Japan)\0" + "Dragon Ball Z - Idainaru Son Gokuu Densetsu (Japan)\0" + "Dragon Half (Japan)\0" + "Dragon Knight & Grafitti (Japan)\0" + "Dragon Knight II (Japan)\0" + "Dragon Knight III (Japan)\0" + "Dragon Slayer - Eiyuu Densetsu (Japan)\0" + "Dragon Slayer - Eiyuu Densetsu II (Japan)\0" + "Dragon Slayer - The Legend of Heroes (USA)\0" + "Dungeon Explorer II (Japan)\0" + "Dungeon Explorer II (USA)\0" + "Dungeon Master - Theron's Quest (Japan)\0" + "Dungeon Master - Theron's Quest (USA)\0" + "Efera & Jiliora - The Emblem from Darkness (Japan)\0" + "Eikan ha Kimini - Koukou Yakyuu Zenkoku Taikai (ACD, SCD)(Japan)\0" + "Eiyuu Sangokushi (Japan)\0" + "Electronic Arts\0" + "Emerald Dragon (Japan)\0" + "Emerald Dragon Taikenban (Japan)\0" + "Excite Software\0" + "Exile (USA)\0" + "Exile - Toki no Hasama he (Japan)\0" + "Exile II - Janen no Jishou (Japan)\0" + "Exile II - Wicked Phenomenon (USA)\0" + "F1 Circus Special - Pole to Win (Japan)\0" + "F1 Team Simulation Project F (Japan)\0" + "FTL Games\0" + "FX Unit Yuki: The Henshin Engine (re-release)\0" + "Faceball (Japan)\0" + "Faceball Taikenban (Japan)\0" + "Faerie Dust Story - Meikyuu no Elfeene (Japan)\0" + "Falcom\0" + "Fantasy Star Soldier (USA, prototype)\0" + "Farjius no Jakoutei - Neo Metal Fantasy (Japan)\0" + "Fausset\303\251 Amour (Japan)\0" + "Fiend Hunter (Japan)\0" + "Fighting Street (Japan)\0" + "Fighting Street (USA)\0" + "Final Zone II (Japan)\0" + "Final Zone II (USA)\0" + "Flash Hiders (Japan)\0" + "Flash Hiders Taikenban (Japan)\0" + "Forgotten Worlds (Japan)\0" + "Forgotten Worlds (USA)\0" + "Formation Soccer '95 - Della Serie A (ACD, SCD)(Japan)\0" + "Fray CD Xak Gaiden (Japan)\0" + "Frozen Utopia\0" + "Fujicom\0" + "Fushigi no Umi no Nadia (Japan)\0" + "GS Mikami (Japan)\0" + "Gain Ground SX (Japan)\0" + "Galaxy Deka Gayvan (Japan)\0" + "Gambler Jikochuushinha - Gekitou 36 Janshi (Japan)\0" + "Gambler Jikochuushinha - Mahjong Puzzle Collection (Japan)\0" + "Games Express\0" + "Garou Densetsu II - Aratanaru Tatakai (Japan)\0" + "Garou Densetsu II - Aratanaru Tatakai - Sample Disc (Japan)\0" + "Garou Densetsu Special (Japan)\0" + "Gate of Thunder (Japan)\0" + "Genocide (Japan)\0" + "Gensou Tairiku Auleria (Japan)\0" + "Ginga Fukei Densetsu Sapphire (Japan)\0" + "Ginga Ojousama Densetsu Yuna (Japan, HRH310827-3FAAT)\0" + "Ginga Ojousama Densetsu Yuna (Japan, HRH310827-3FAFT)\0" + "Ginga Ojousama Densetsu Yuna HuVideo CD (Japan)\0" + "Ginga Ojousama Densetsu Yuna II - Eternal Princess (Japan)\0" + "Go! Go! Birdie Chance (Japan)\0" + "God Panic - Shijou Saikyou Gundan (Japan)\0" + "Godzilla (USA)\0" + "Godzilla - Bakutou Retsuden (Japan)\0" + "Golden Axe (Japan)\0" + "Gradius II - Gofer no Yabou (Japan)\0" + "Gulclight TDF 2 (Japan)\0" + "G\303\266tzendiener (Japan)\0" + "Hatsukoi Monogatari (Japan)\0" + "Hawiian Island Girls (USA)\0" + "Hawk F-123 (Japan)\0" + "Hellfire S - The Another Story (Japan)\0" + "Henshin Engine (demo)\0" + "Hi-Leg Fantasy (GECD)(Japan)\0" + "High Grenadier (Japan)\0" + "Hihou Densetsu Chris no Bouken (Japan)\0" + "Himitsu no Hanazono (Japan)\0" + "Horror Story (Japan)\0" + "Hu PGA Tour Power Golf 2 - Golfer (Japan)\0" + "Hudson\0" + "Hudson Soft\0" + "Hudson Soft / Red\0" + "Human\0" + "Human Entertainment\0" + "Human Sports Festival (Japan)\0" + "Hyaku Monogatari - Hontou ni Atta Kowai Hanashi (Japan)\0" + "Hyper Dyne SideArms Special (Japan)\0" + "Hyper Wars (Japan)\0" + "Hypernova Blast (USA)\0" + "ICOM Simulations\0" + "IGS\0" + "IQ Panic (Japan)\0" + "Iga Ninden Gaou (Japan)\0" + "Image Fight II (Japan)\0" + "Implode (USA)\0" + "Inoue Mami - Kono Hoshi ni Tatta Hitori no Kimi (Japan)\0" + "Insanity\0" + "Intec\0" + "Inter State\0" + "Irem\0" + "It Came from the Desert (USA)\0" + "J Thunder (Japan)\0" + "J. B. Harold Murder Club (USA)\0" + "J. B. Harold Satsujin Club (Japan)\0" + "J. League Tremendous Soccer '94 (ACD, SCD)(Japan)\0" + "Jack Nicklaus Turbo Golf (USA)\0" + "Jack Nicklaus World Tour Golf (Japan)\0" + "Janshin Densetsu - Quest of Jongmaster (Japan)\0" + "Jantei Monogatari (Japan)\0" + "Jantei Monogatari II - Uchuu Tantei Divan Kanketsu Hen (Japan)\0" + "Jantei Monogatari II - Uchuu Tantei Divan Shutsudou Hen (Japan)\0" + "Jantei Monogatari III - Saver Angels (Japan)\0" + "Japan\0" + "Japan/World\0" + "Jessie Jaeger in Cleopatra's Curse\0" + "Jessie Jaeger in Cleopatra's Curse (demo)\0" + "Jim Power - In Mutant Planet (Japan)\0" + "John Madden Duo CD Football (USA)\0" + "Juuouki (Japan)\0" + "KO Seiki Beast - Gaia Fukkatsu Kanketsu Hen (Japan)\0" + "Kagami no Kuni no Legend (Japan)\0" + "Kaizou Choujin Shubibinman III - Ikai no Princess (Japan)\0" + "Kakutou Haou Densetsu Algunos (Japan)\0" + "Kawa no Nushi Tsuri - Shizenha (Japan)\0" + "Kaze Kiri - Ninja Action (Japan)\0" + "Kaze no Densetsu Xanadu (Japan)\0" + "Kaze no Densetsu Xanadu II (Japan)\0" + "Kiaidan 00 (Japan)\0" + "Kidou Keisatsu Patlabor - Griffon Hen (Japan)\0" + "King Records\0" + "Kisou Louga (Japan)\0" + "Kisou Louga II - The Ends of Shangrila (Japan)\0" + "Koei\0" + "Kogado Studio\0" + "Konami\0" + "Kuusou Kagaku Sekai Gulliver Boy (Japan)\0" + "L-Dis (Japan)\0" + "Lady Phantom (Japan)\0" + "Langrisser - Hikari no Matsuei (Japan)\0" + "Laplace no Ma (Japan)\0" + "Laplace no Ma - Preview Disk (Japan)\0" + "Laser Soft\0" + "Laser Soft Visual Collection Volume I - Cosmic Fantasy Visual Shuu (Japan)\0" + "Laser Soft Visual Collection Volume II - Valis Visual Shuu (Japan)\0" + "Last Alert (USA)\0" + "Last Armageddon (Japan, BRCD0001-6-0625-R1F)\0" + "Last Armageddon (Japan, BRCD0001-7-0904-R1D)\0" + "Legion (Japan)\0" + "Lemmings (Japan)\0" + "Linda\302\263 (ACD, SCD)(Japan)\0" + "Lodoss Tousenki - Record of Lodoss War (Japan)\0" + "Lodoss Tousenki II - Record of Lodoss War II (Japan)\0" + "Loom (Japan)\0" + "Loom (USA)\0" + "Lord of Wars (Japan)\0" + "Lords of Thunder (USA)\0" + "Lords of the Rising Sun (USA)\0" + "Lucas Arts\0" + "Mad Stalker - Full Metal Force (Japan)\0" + "Mad Stalker - Full Metal Force - Sample Disc (Japan)\0" + "Madou Monogatari I - Honou no Sotsuenji (Japan)\0" + "Magical Dinosaur Tour (USA)\0" + "Magical Fantasy Adventure - Popful Mail (ACD, SCD)(Japan)\0" + "Magical Saurus Tour (Japan)\0" + "Magicoal (Japan)\0" + "Mahjong Clinic Special (Japan)\0" + "Mahjong Lemon Angel (Japan)\0" + "Mahjong Sword - Princess Quest Gaiden (ACD, SCD)(Japan)\0" + "Mahjong Vanilla Syndrome (CD, SCD)(Japan)\0" + "Mahjong on the Beach (Japan)\0" + "Mahou no Shoujo Silky Lip (Japan, prototype Test Wo. Ver 3.1)\0" + "Mahou no Shoujo Silky Lip (Japan, prototype v5.0)\0" + "Mamono Hunter Youko - Makai kara no Tenkousei (Japan)\0" + "Mamono Hunter Youko - Tooki Yobigoe (Japan)\0" + "Martial Champion (Japan)\0" + "Mashou Denki La Valeur (Japan)\0" + "Master of Monsters (Japan)\0" + "Mateki Densetsu Astralius (Japan)\0" + "Maxis\0" + "Media Rings Corporation\0" + "Megami Paradise (Japan)\0" + "Metal Angel (Japan)\0" + "Metal Angel II (Japan)\0" + "Metamor Jupiter (Japan)\0" + "Meteor Blaster DX (USA)\0" + "Meteor Blaster DX (USA, re-release)\0" + "Micro Cabin\0" + "Micro World\0" + "Micronet\0" + "Might and Magic (Japan)\0" + "Might and Magic III - Isles of Terra (Japan)\0" + "Might and Magic III - Isles of Terra (USA)\0" + "MindRec\0" + "MindRec & VodSound\0" + "Mine Sweeper (Japan)\0" + "Mirai Shonen Conan (Japan)\0" + "Mitsubachi Gakuen (Japan)\0" + "Monster Lair (USA)\0" + "Monster Maker - Yami no Ryuukishi (Japan)\0" + "Moonlight Lady (Japan)\0" + "Motoroader MC (Japan)\0" + "Motteke Tamago (Japan)\0" + "Mugen Senshi Valis - Legend of a Fantasm Soldier (Japan)\0" + "Mysterious Song\0" + "Mysterious Song (re-release)\0" + "Mystic Formula (Japan)\0" + "NCS\0" + "NEC\0" + "NEC Avenue\0" + "NEC Home Electronics\0" + "NEC Interchannel\0" + "Naxat Soft\0" + "Nekketsu Koukou Dodgeball Bu - Soccer Hen (Japan)\0" + "Nekketsu Koushinkyoku - Soreyuke Daiundoukai (Japan)\0" + "Nekketsu Legend Baseballer (Japan)\0" + "Nemurenumori no Chiisana Ohanashi (Japan)\0" + "Neo Nectaris (Japan)\0" + "New World Computing\0" + "Nexzr (Japan)\0" + "Nichibutsu\0" + "Nicole Express\0" + "Nihon Falcom\0" + "Nihon Telenet\0" + "Nishimura Kyoutarou Mystery - Hokutosei no Onna (Japan)\0" + "No Ri Ko (Japan)\0" + "Nobunaga no Yabou - Bushou Fuuunroku (Japan)\0" + "Nobunaga no Yabou - Zenkokuban (Japan)\0" + "Orion / Arm / Gregg\0" + "PCEngine Fan Special CD-ROM Volume I (Japan)\0" + "PCEngine Hyper Catalog (CD-ROM Capsule) (Japan)\0" + "PCEngine Hyper Catalog Duo-RX - Disc A (Japan)\0" + "PCEngine Hyper Catalog Duo-RX - Disc B (Japan)\0" + "PCEngine Hyper Catalog II (Japan)\0" + "PCEngine Hyper Catalog III (Japan)\0" + "PCEngine Hyper Catalog IV (Japan)\0" + "PCEngine Hyper Catalog V (Japan)\0" + "PCEngine Hyper Catalog VI - Disc A (Japan)\0" + "PCEngine Hyper Catalog VI - Disc B (Japan)\0" + "Pachio-kun - Maboroshi no Densetsu (Japan)\0" + "Pachio-kun - Warau Uchuu (Japan)\0" + "Pachio-kun III - Pachisuro Pachinko (Japan)\0" + "Pack-In-Video\0" + "Paradion - Auto Crusher Palladium (Japan)\0" + "Pastel Lime (Japan)\0" + "Pixel Heart\0" + "Police Connection (Japan)\0" + "Pomping World (Japan)\0" + "Pop'n Magic (Japan)\0" + "Populous - The Promised Lands (Japan)\0" + "Prince of Persia (Japan)\0" + "Prince of Persia (USA)\0" + "Princess Maker 1 (Japan)\0" + "Princess Maker 2 (ACD, SCD)(Japan)\0" + "Princess Minerva (Japan)\0" + "Private eye dol (ACD, SCD)(Japan)\0" + "Psychic Detective Series Vol. III - Aya (Japan)\0" + "Psychic Detective Series Vol. III - Aya - Auto Demo (Japan)\0" + "Psychic Detective Series Vol. IV - Orgel (Japan)\0" + "Psychic Storm (Japan)\0" + "Psygnosis\0" + "Puyo Puyo CD (Japan)\0" + "Puyo Puyo CD Tsuu (Japan)\0" + "Pyramid Plunder\0" + "Quiz Avenue (Japan)\0" + "Quiz Avenue II (Japan)\0" + "Quiz Avenue III (Japan)\0" + "Quiz Caravan Cult Q (Japan)\0" + "Quiz Marugoto The World II - Time Machine ni Onegai! (Japan)\0" + "Quiz Marugoto the World (Japan)\0" + "Quiz Tonosama no Yabou (Japan)\0" + "Quiz de Gakuensai (Japan)\0" + "Quiz no Hoshi (Japan)\0" + "R-Type Complete CD (Japan)\0" + "Rainbow Islands (Japan)\0" + "Ranma \302\275 (Japan)\0" + "Ranma \302\275 - Datou, Ganso Musabetsu Kakutou-Ryuu! (CD, SCD)(Japan)\0" + "Ranma \302\275 - Toraware no Hanayome (Japan)\0" + "Ray Force\0" + "Rayxanber II (Japan)\0" + "Rayxanber III (Japan)\0" + "Red Alert (Japan)\0" + "Renny Blaster (Japan)\0" + "Revival Chase\0" + "Revival Games\0" + "Right Stuff\0" + "Riot Zone (USA)\0" + "Rising Sun (Japan)\0" + "Riverhill Soft\0" + "Riverhill Software\0" + "Road Spirits (Japan)\0" + "Rom Rom Stadium (Japan)\0" + "Rom\302\262 Karaoke - Volume I (Japan)\0" + "Rom\302\262 Karaoke - Volume I - Suteki ni Standard (Japan)\0" + "Rom\302\262 Karaoke - Volume II (Japan)\0" + "Rom\302\262 Karaoke - Volume II - Nattoku Idol (Japan)\0" + "Rom\302\262 Karaoke - Volume III (Japan)\0" + "Rom\302\262 Karaoke - Volume III - Yappashi Band (Japan)\0" + "Rom\302\262 Karaoke - Volume IV (Japan)\0" + "Rom\302\262 Karaoke - Volume IV - Choito Otona! (Japan)\0" + "Rom\302\262 Karaoke - Volume V (Japan)\0" + "Rom\302\262 Karaoke - Volume V - Maku no Uchi (Japan)\0" + "Ruin - Kami no Isan (Japan)\0" + "Ryuuko no Ken (Japan)\0" + "Ryuuko no Ken - Sample Disc (Japan)\0" + "Sangokushi Eiketsu - Tenka ni Nozomu (Japan)\0" + "Sangokushi III (Japan)\0" + "Seirei Senshi Spriggan (Japan)\0" + "Seiryuu Densetsu Monbit (Japan)\0" + "Seisenshi Denshou - Jantaku no Kishi (Japan)\0" + "Seiya Monogatari - Anearth Fantasy Stories (Japan)\0" + "Seiya Monogatari - Anearth Fantasy Stories (Japan, debug prototype)\0" + "Seiya Monogatari - Anearth Fantasy Stories Taikenban (Japan)\0" + "Sengoku Kantou Sangokushi (Japan)\0" + "Seta\0" + "Sexy Idol Mahjong (Japan)\0" + "Sexy Idol Mahjong - Fashion Monogatari (ACD, SCD)(Japan)\0" + "Sexy Idol Mahjong - Yakyuuken no Uta (Japan)\0" + "Shadow of the Beast (USA)\0" + "Shadow of the Beast - Majou no Okite (Japan)\0" + "Shanghai II (Japan)\0" + "Shanghai III - Dragon's Eye (Japan)\0" + "Shape Shifter (USA)\0" + "Shapeshifter - Makai Eiyuu Den (Japan)\0" + "Sherlock Holmes Consulting Detective (USA)\0" + "Sherlock Holmes Consulting Detective Volume II (CD, SCD)(USA)\0" + "Sherlock Holmes no Tantei Kouza (Japan)\0" + "Sherlock Holmes no Tantei Kouza II (Japan)\0" + "Shiawase Usagi - Nureta Bishoujo Ichi Hajimete na no ni (Japan)\0" + "Shiawase Usagi II - Toraware Usagi Sailor Z (Japan)\0" + "Shin Megami Tensei (Japan)\0" + "Shin Nihon Pro Wrestling Kounin - '94 Battlefield in Tokyo Dome (Japan)\0" + "Shin Onryou Senki (Japan)\0" + "Shin Sangokushi - Tenka ha Ware ni (Japan)\0" + "Shinsetsu Shiawase Usagi (Japan)\0" + "Shinsetsu Shiawase Usagi 2 - Kairaku e no Invitation (Japan)\0" + "Shinsetsu Shiawase Usagi f - Yuujou Yori mo Aiyoku (Japan)\0" + "Shogakukan\0" + "Shougi Database Kiyuu (Japan)\0" + "Sim Earth - The Living Planet (Japan)\0" + "Sim Earth - The Living Planet (USA)\0" + "Sleuth Publications\0" + "Slime World (Japan)\0" + "Slot Shoubushi (Japan)\0" + "Snatcher (Japan)\0" + "Snatcher - Pilot Disk (Japan)\0" + "Sofix\0" + "Sol Bianca (Japan)\0" + "Sol Moonarge (Japan)\0" + "Solid Force (Japan)\0" + "Sony Music Entertainment\0" + "Sorcerian (Japan)\0" + "Sotsugyou - Graduation (Japan)\0" + "Sotsugyou II - Neo Generation (ACD, SCD)(Japan)\0" + "Sotsugyou Shashin - Miki (Japan)\0" + "Space Ava 201: Quantum Field Theory\0" + "Space Fantasy Zone (CD, SCD)(Japan, prototype)\0" + "Space Invaders - The Original Game (Japan)\0" + "Splash Lake (Japan)\0" + "Splash Lake (USA)\0" + "Spriggan Mark II - Re Terraform Project (Japan)\0" + "Star Breaker (Japan)\0" + "Star Mobile (Japan)\0" + "Star Parodia (Japan)\0" + "Startling Odyssey (Japan)\0" + "Startling Odyssey II - Makyou Sensou (Japan)\0" + "Steam Heart's (Japan)\0" + "Strider Hiryuu (Japan)\0" + "Success\0" + "Sugoroku '92 Nari Tore Nariagari Trendy (Japan)\0" + "Summer Carnival '92 - Alzadick (Japan)\0" + "Summer Carnival '93 - Nexzr Special (Japan)\0" + "SunSoft\0" + "Sunsoft\0" + "Super Air Zonk (USA)\0" + "Super Albatross (Japan)\0" + "Super CD-ROM\302\262 Taiken Soft-shuu (Japan)\0" + "Super Daisenryaku (Japan)\0" + "Super Darius (Japan)\0" + "Super Darius II (Japan)\0" + "Super Mahjong Taikai (Japan)\0" + "Super Raiden (Japan)\0" + "Super Real Mahjong P II III Custom (ACD, SCD)(Japan)\0" + "Super Real Mahjong P IV Custom (Japan)\0" + "Super Real Mahjong P V Custom (ACD, SCD)(Japan)\0" + "Super Real Mahjong Special - Miki Kasumi Shoko no Omoide (Japan)\0" + "Super Schwartzschild (CD, SCD)(Japan)\0" + "Super Schwartzschild II (Japan)\0" + "Sword Master (Japan)\0" + "Syd Mead\0" + "Syd Mead's Terra Forming (CD, SCD)(USA)\0" + "Sylphia (Japan)\0" + "System Soft\0" + "TGL\0" + "Tadaima Yuusha Boshuuchuu (Japan)\0" + "Tadaima Yuusha Boshuuchuu - Sample Disc (Japan)\0" + "Taiheiki (Japan)\0" + "Taito\0" + "Tanjou Debut (ACD, SCD)(Japan)\0" + "Tecmo World Cup Super Soccer (Japan)\0" + "Teichiku Records\0" + "Tekipaki Working Love (Japan)\0" + "Telenet Japan\0" + "Tenchi Muyou! Ryououki (Japan)\0" + "Tenchi wo Kurau (Japan)\0" + "Tengai Makyou - Fuu'un Kabuki Den Shutsugeki no Sho (Japan)\0" + "Tengai Makyou - Kabuki Ittou Ryoudan (Japan)\0" + "Tengai Makyou - Ziria (Japan)\0" + "Tengai Makyou - Ziria Hibaihin (Japan)\0" + "Tengai Makyou II - Manji Maru (Japan)\0" + "Tenshi no Uta (Japan)\0" + "Tenshi no Uta II - Datenshi no Uta (Japan)\0" + "Terraforming (CD, SCD)(Japan)\0" + "The Addams Family (USA)\0" + "The Atlas - Renaissance Voyager (ACD, SCD)(Japan)\0" + "The Davis Cup Tennis (Japan)\0" + "The Dynastic Hero (USA)\0" + "The Kick Boxing (Japan)\0" + "The Local Girls of Hawaii (USA)\0" + "The Manhole (Japan)\0" + "The Pro Yakyuu (Japan)\0" + "The Pro Yakyuu Super '94 (Japan)\0" + "The Pro Yakyuu Super (Japan)\0" + "The Sarumaru Company\0" + "The TV Show (Japan)\0" + "Toho\0" + "Tokimeki Memorial (Japan, HRKM70414-1FAAT)\0" + "Tokimeki Memorial (Japan, HRKM70701-2FAAT)\0" + "Tokimeki Memorial (Japan, HRKM71014-3FAAT)\0" + "Tokuma Shoten\0" + "Tonkin House\0" + "Top wo Nerae! GunBuster Volume I (Japan)\0" + "Top wo Nerae! GunBuster Volume II (Japan)\0" + "Travel Epuru (Japan)\0" + "Travelers! Densetsu wo Buttobase (Japan)\0" + "USA\0" + "Uchuu Senkan Yamato (Japan)\0" + "Ultimate Rally Club\0" + "Ultrabox II Gou (Japan)\0" + "Ultrabox III Gou (Japan)\0" + "Ultrabox IV Gou (Japan)\0" + "Ultrabox Sohkan Gou (Japan)\0" + "Ultrabox V Gou (Japan)\0" + "Ultrabox VI Gou (Japan)\0" + "Urusei Yatsura - Stay With You (Japan)\0" + "Valis II (USA)\0" + "Valis II - The Fantasm Soldier (Japan)\0" + "Valis III (USA)\0" + "Valis III - The Fantasm Soldier (Japan)\0" + "Valis IV - The Fantasm Soldier (Japan)\0" + "Vasteel (Japan)\0" + "Vasteel (USA)\0" + "Vasteel II (ACD, SCD)(Japan)\0" + "Victor Entertainment\0" + "Victor Musical Industries\0" + "Virgin Dream (Japan)\0" + "Westone\0" + "Where in the World is Carmen Sandiego? (Japan)\0" + "Winds of Thunder (Japan)\0" + "Wizardry I II (Japan)\0" + "Wizardry III IV (Japan)\0" + "Wizardry V - Heart of the Maelstrom (Japan)\0" + "Wonder Boy III - Monster Lair (Japan)\0" + "World\0" + "World Heroes II (Japan)\0" + "World Heroes II - Sample Disc (Japan)\0" + "Wrestling Angels - Double Impact (ACD, SCD)(Japan)\0" + "Wrestling Universe - Fire Pro Joshi - Dome Choujo Taisen - WOWOW vs. JWP (Japan)\0" + "Xak I & II (Japan)\0" + "Xak III - The Eternal Recurrence (Japan)\0" + "Yamamura Misa Suspense - Kinsenka Kyou Ezara Satsujin Jiken (Japan)\0" + "Yami no Ketsuzoku Harukanaru Kioku (Japan)\0" + "Yawara! (Japan)\0" + "Yawara! II (Japan)\0" + "Yazawa Science Office\0" + "Ys Book I II (Japan)\0" + "Ys Book I II (USA)\0" + "Ys III - Wanderers from Ys (Japan)\0" + "Ys III - Wanderers from Ys (USA)\0" + "Ys IV - The Dawn of Ys (Japan, HCD3051-4-1108-R1F)\0" + "Ys IV - The Dawn of Ys (Japan, HCD3051-5-1116-R1P)\0" + "Yu Yu Hakusho (Japan)\0" + "Zan - Kagerou no Toki (Japan)\0" + "Zero 4 Champ II (Japan)\0" + "Zero Wing (Japan)\0" + "addfam\0" + "advquiz\0" + "aichoan\0" + "aiii\0" + "algunos\0" + "alkiba\0" + "alshark\0" + "anesan\0" + "angels2\0" + "asuka120\0" + "atlas\0" + "auleria\0" + "auroraq\0" + "avenger\0" + "avgeo\0" + "avtanjo\0" + "babel\0" + "babyjo\0" + "bakuhunt\0" + "bakushou\0" + "bakuzone\0" + "basted\0" + "bazaru\0" + "bbros\0" + "beast\0" + "beastj\0" + "beyshadg\0" + "bikini\0" + "bikkurid\0" + "blackh\0" + "blodgear\0" + "bnzabros\0" + "bombmn94\0" + "bonk3\0" + "brandish\0" + "browning\0" + "buildlnd\0" + "burai\0" + "burai2\0" + "cal2\0" + "cal3\0" + "campcali\0" + "cardangl\0" + "carmnwld\0" + "cdbattle\0" + "cddenjin\0" + "cdsys\0" + "chiki\0" + "choaniki\0" + "chrally\0" + "chrisbkn\0" + "cobra\0" + "cobra2\0" + "colorwar\0" + "conan\0" + "cosmfn4g\0" + "cosmfn4t\0" + "cosmfnt\0" + "cosmfnt2\0" + "cosmfnt2j\0" + "cosmfnt3\0" + "cotton\0" + "cottonj\0" + "crazyhos\0" + "crestwlf\0" + "cyber808\0" + "daisenpu\0" + "daisenr2\0" + "daviscup\0" + "dbz\0" + "ddragon2\0" + "deathbrn\0" + "dedbrain\0" + "dedenden\0" + "deja\0" + "dekoboko\0" + "dennoten\0" + "devbasic\0" + "devmag1\0" + "devstart\0" + "dknight2\0" + "dknight3\0" + "dknightg\0" + "dmaster\0" + "dmasterj\0" + "dokyusei\0" + "doraemon\0" + "downld2\0" + "downtown\0" + "draculax\0" + "draghalf\0" + "dslayed2\0" + "dslayedj\0" + "dslaylh\0" + "dungexp2\0" + "dungexp2j\0" + "dynhero\0" + "dynheroj\0" + "eferajil\0" + "eikankim\0" + "eiyu3gok\0" + "elfeen\0" + "emeraldd\0" + "emeralddt\0" + "exile\0" + "exile2\0" + "exile2j\0" + "exilej\0" + "f1circus\0" + "f1team\0" + "faceball\0" + "faceballt\0" + "fanspc1\0" + "faussete\0" + "fiendhun\0" + "fireproj\0" + "flashhid\0" + "flashhidt\0" + "forgottn\0" + "forgottnj\0" + "fraycd\0" + "fsoccr95\0" + "fssoldr\0" + "fstreet\0" + "fstreetj\0" + "fzone2\0" + "fzone2j\0" + "galaxyde\0" + "gambler\0" + "gamblrpz\0" + "garou2\0" + "garou2s\0" + "garousp\0" + "gatethun\0" + "genchohi\0" + "genocide\0" + "gground\0" + "godpanic\0" + "godzilla\0" + "godzillaj\0" + "gogobird\0" + "goldnaxe\0" + "gotzendi\0" + "gradius2\0" + "gsmikami\0" + "gulcligh\0" + "gulliver\0" + "gunbust1\0" + "gunbust2\0" + "hanafuda\0" + "hatsukoi\0" + "hawaiiu\0" + "hawiian\0" + "hawkf123\0" + "hellfire\0" + "henshin\0" + "henshin_d\0" + "highgrnd\0" + "hilegfan\0" + "himitsu\0" + "holmes\0" + "holmes2\0" + "holmes2j\0" + "holmesj\0" + "horrorst\0" + "humanspt\0" + "hupga2\0" + "hyakumon\0" + "hypercap\0" + "hyperdyn\0" + "hypernov\0" + "hyperwar\0" + "hyprcat2\0" + "hyprcat3\0" + "hyprcat4\0" + "hyprcat5\0" + "hyprcata\0" + "hyprcatb\0" + "hyprct6a\0" + "hyprct6b\0" + "idolpai\0" + "iganin\0" + "imagef2\0" + "implode\0" + "inouemam\0" + "insanity\0" + "iqpanic\0" + "itcame\0" + "jantei\0" + "jantei2k\0" + "jantei2s\0" + "jantei3\0" + "jbharold\0" + "jbharoldj\0" + "jimpower\0" + "jjcleopa\0" + "jjcleopa_d\0" + "jleagt94\0" + "jongmstr\0" + "jthunder\0" + "juuouki\0" + "kagami\0" + "karao1ss\0" + "karao2ni\0" + "karao3yb\0" + "karao4co\0" + "karao5mu\0" + "karaoke1\0" + "karaoke2\0" + "karaoke3\0" + "karaoke4\0" + "karaoke5\0" + "kawanus\0" + "kazekiri\0" + "kiaidan\0" + "kickbox\0" + "kisoulo2\0" + "kisoulou\0" + "koseikib\0" + "ladyphan\0" + "langriss\0" + "laplace\0" + "laplacep\0" + "lastarmg\0" + "lastarmg2\0" + "lavaleur\0" + "ldis\0" + "legion\0" + "lemmings\0" + "linda3\0" + "lodoss\0" + "lodoss2\0" + "loom\0" + "loomj\0" + "lordrsun\0" + "lordthun\0" + "lordwars\0" + "lsoft1\0" + "lsoft2\0" + "lstalert\0" + "macr2036\0" + "macrosls\0" + "madden\0" + "madoum\0" + "madstlkr\0" + "madstlkrs\0" + "magdino\0" + "magicoal\0" + "magsaurs\0" + "mahjong\0" + "mamonoht\0" + "mamonohu\0" + "manhole\0" + "mastmons\0" + "matekida\0" + "megamipa\0" + "metalan2\0" + "metalang\0" + "metamorj\0" + "meteorbl\0" + "meteorbl_10\0" + "metlfant\0" + "mightmag\0" + "mightmg3\0" + "mightmg3j\0" + "mineswep\0" + "mitsubac\0" + "mjbeach\0" + "mjclinic\0" + "mjlemang\0" + "mjsword\0" + "mlair\0" + "mlairj\0" + "monbit\0" + "monstmkr\0" + "moonligh\0" + "motoroad\0" + "motteke\0" + "mtlchamp\0" + "mysticf\0" + "mystsong\0" + "mystsong_a\0" + "nadia\0" + "neklegbb\0" + "neksoccr\0" + "nemurenu\0" + "neonectr\0" + "nexzr\0" + "nicklaus\0" + "nicklausj\0" + "nkhokuon\0" + "nobubufu\0" + "nobuzenk\0" + "noriko\0" + "pachikn2\0" + "pachikn3\0" + "pachikun\0" + "pachinko\0" + "pachislt\0" + "panicbom\0" + "paradion\0" + "pastelli\0" + "patlabor\0" + "pce:acardpro\0" + "pce:gecd\0" + "policeco\0" + "pompingw\0" + "popful\0" + "popnmagi\0" + "populous\0" + "ppersia\0" + "ppersiaj\0" + "princesm\0" + "prinmak\0" + "prinmak2\0" + "priveyed\0" + "proyak94\0" + "proyaks\0" + "proyakyu\0" + "psycdet3\0" + "psycdet3d\0" + "psycdet4\0" + "psychics\0" + "puyopuy2\0" + "puyopuyo\0" + "pyrplund\0" + "quizaven\0" + "quizavn2\0" + "quizavn3\0" + "quizcara\0" + "quizgaku\0" + "quizmar2\0" + "quizmaru\0" + "quiznoho\0" + "quiztono\0" + "ranma\0" + "ranmadat\0" + "ranmator\0" + "rayxan2\0" + "rayxan3\0" + "rbisland\0" + "redalert\0" + "rennybla\0" + "revchase\0" + "riotzone\0" + "risingsn\0" + "roadspir\0" + "romstad\0" + "rtype\0" + "ruin\0" + "ryukoken\0" + "ryukokens\0" + "sailormc\0" + "sailormn\0" + "sangoku\0" + "sangoku3\0" + "sapphire\0" + "scdrpgs\0" + "scdsys\0" + "sdarius\0" + "sdarius2\0" + "seisensh\0" + "seiyamon\0" + "seiyamonp\0" + "seiyamont\0" + "sengokuk\0" + "sexyidof\0" + "sexyidol\0" + "sexyidoy\0" + "shangh2\0" + "shangh3\0" + "shapesh\0" + "shapeshj\0" + "shiawas2\0" + "shiawase\0" + "shinmt\0" + "shinonry\0" + "shinsang\0" + "shinset2\0" + "shinsetf\0" + "shinsets\0" + "shougidb\0" + "shubibi3\0" + "silkylip\0" + "silkylipo\0" + "simearth\0" + "simearthj\0" + "slimew\0" + "slotshou\0" + "snatcher\0" + "snatcherpd\0" + "solbianc\0" + "solidfrc\0" + "solmoona\0" + "sorcer\0" + "soreyukd\0" + "sotsugy2\0" + "sotsugym\0" + "sotsugyo\0" + "spaceava\0" + "spaceinv\0" + "spcfzone\0" + "splashl\0" + "splashlj\0" + "spriggan\0" + "spriggn2\0" + "sprowr94\0" + "sraiden\0" + "srmp23\0" + "srmp4\0" + "srmp5\0" + "srmspec\0" + "starbrkr\0" + "starmobi\0" + "starparo\0" + "startod2\0" + "startody\0" + "steamhea\0" + "strider\0" + "sugoroku\0" + "summer92\0" + "summer93\0" + "supdaisn\0" + "superair\0" + "superalb\0" + "supermjt\0" + "supersc2\0" + "supersch\0" + "swordmst\0" + "sylphia\0" + "tadaima\0" + "tadaimas\0" + "taiheiki\0" + "tanjodeb\0" + "tecmowc\0" + "tekiwork\0" + "tenchi\0" + "tenchiku\0" + "tengaim2\0" + "tengaima\0" + "tengaims\0" + "tengaimz\0" + "tengaimzh\0" + "tenshiu2\0" + "tenshiut\0" + "terrafor\0" + "terraforj\0" + "tokimeki\0" + "tokimekib\0" + "tokimekic\0" + "travelep\0" + "traveler\0" + "tvshow\0" + "ultrago2\0" + "ultrago3\0" + "ultrago4\0" + "ultrago5\0" + "ultrago6\0" + "ultrasgo\0" + "urclub\0" + "uruseiya\0" + "valis\0" + "valis2\0" + "valis2j\0" + "valis3\0" + "valis3j\0" + "valis4\0" + "vanilla\0" + "vasteel\0" + "vasteel2\0" + "vasteelj\0" + "virgindr\0" + "wh2\0" + "wh2s\0" + "windthun\0" + "wizard12\0" + "wizard34\0" + "wizardr5\0" + "wrestang\0" + "xak12\0" + "xak3\0" + "xanadu\0" + "xanadu2\0" + "yamamura\0" + "yamato\0" + "yamikets\0" + "yawara\0" + "yawara2\0" + "ys12\0" + "ys12j\0" + "ys3\0" + "ys3j\0" + "ys4\0" + "ys4a\0" + "yuna\0" + "yuna2\0" + "yunaa\0" + "yunahv\0" + "yuyu\0" + "zan\0" + "zero4c2\0" + "zerowing\0" +; + +static const PceCdGame pceCdGames[504] = { + {{0x00,0x83,0xf1,0x5f,0x77,0x63,0x02,0x73,0xa3,0x97,0x78,0x3a,0xf0,0x34,0x40,0x74,0xc1,0x71,0x6f,0x35},18414,1877,67,6140,16652,0,21138,2}, + {{0x00,0xae,0x7b,0xad,0xc2,0x41,0xf9,0x2a,0x7a,0x36,0x2f,0xd4,0xc7,0x51,0x95,0xf8,0x1d,0x58,0xbf,0xa7},18834,3471,62,5914,6805,0,21138,0}, + {{0x00,0xe5,0x61,0x7b,0xb5,0x0d,0x9d,0x27,0xd1,0xef,0x5c,0x14,0xc6,0xfa,0x5f,0xc2,0xbe,0x57,0x9d,0x76},19462,5851,67,9652,6805,0,21138,0}, + {{0x01,0x99,0x18,0xbf,0xa9,0xfb,0xb5,0x0e,0xc3,0x86,0x84,0x6f,0x42,0xa1,0x3d,0x94,0xa8,0x1d,0xc2,0xbb},22008,16828,62,17099,6805,0,18459,0}, + {{0x02,0x72,0xc7,0xc9,0x46,0x45,0x1b,0xcc,0x51,0x36,0xaf,0x32,0xa0,0x91,0x10,0x44,0xb6,0xa8,0xc4,0x68},18150,563,72,17099,6805,0,21138,0}, + {{0x02,0x74,0xac,0x68,0xec,0x08,0x2e,0xbd,0x34,0xab,0x66,0xcb,0x12,0xac,0x29,0xbd,0x94,0xce,0xbb,0xfc},20357,8450,67,9701,6805,0,21138,0}, + {{0x03,0xa0,0xfd,0x68,0x59,0x53,0x5e,0x80,0xad,0x70,0xd4,0x94,0xef,0x9d,0x9f,0x1d,0xc3,0x22,0xad,0x59},19568,10270,102,13847,6805,0,21138,0}, + {{0x04,0xb1,0x28,0x12,0xdb,0x56,0x50,0xe4,0x0b,0x05,0x6e,0x72,0x42,0x0a,0x6e,0xb2,0x11,0xe2,0x85,0xf0},19047,4483,67,11880,6805,0,21138,1}, + {{0x05,0x06,0x4b,0x59,0x9c,0xcf,0xe5,0xe8,0x6f,0x3d,0x25,0xa4,0x6e,0xab,0xc0,0x12,0x26,0xc4,0x16,0xba},21741,15249,51,7457,6805,0,18459,0}, + {{0x05,0x12,0xda,0x6b,0x6c,0xf4,0x77,0x3b,0xd8,0xfa,0xda,0x01,0xfe,0xe1,0xde,0x44,0x92,0xf3,0x70,0x9a},21853,15710,67,5914,6805,0,21138,0}, + {{0x06,0x9b,0x40,0x2f,0xa1,0xd9,0xdb,0x6e,0xb0,0xc3,0x62,0x37,0x18,0x1f,0x4c,0xfc,0x2c,0xa5,0xf2,0xe2},21678,14739,62,9701,6805,0,18459,0}, + {{0x06,0xa0,0x11,0x38,0x8f,0xf2,0xa2,0x43,0xc2,0xd2,0x2a,0x2c,0x92,0x02,0x28,0x16,0xac,0xb0,0x53,0x6b},19729,6823,152,1251,17375,0,21138,0}, + {{0x07,0x64,0x08,0x52,0x4f,0xd1,0xb9,0xce,0xc8,0x67,0x14,0x41,0x88,0x65,0xbf,0xa9,0x54,0xdf,0x76,0x32},20167,8433,67,9663,6805,0,21138,0}, + {{0x07,0xc8,0x85,0x0f,0xe1,0xef,0xa4,0x77,0x5a,0x5f,0x5f,0x9d,0x6b,0x81,0x1a,0x27,0x94,0x57,0x8c,0x09},20447,9621,67,9144,6805,0,21138,0}, + {{0x08,0x58,0xc0,0xd7,0x92,0x05,0x3a,0x24,0xed,0x2d,0xf9,0xcc,0xf3,0x4c,0x6f,0xb1,0xfb,0xe9,0xe9,0xf2},19109,4419,41,1899,16652,0,18459,0}, + {{0x08,0x5e,0xca,0xb0,0xe1,0x6b,0x7e,0x1f,0x74,0x84,0x3d,0x14,0xa4,0xf8,0xff,0xe3,0x6a,0x63,0x56,0x7d},20099,7805,46,15641,16652,0,18459,4}, + {{0x08,0xdd,0x0f,0x25,0xd9,0x00,0xac,0xbb,0xbb,0xf3,0x37,0xbc,0x73,0x4c,0xb3,0xe3,0x96,0xa5,0xfc,0x48},20349,8607,67,9652,6805,0,21138,0}, + {{0x09,0x87,0xaa,0xdb,0x3d,0xad,0x46,0x4d,0x8a,0xc1,0xbb,0x22,0x2b,0x9a,0xf6,0xb4,0xe7,0x68,0xb6,0x28},21236,13107,46,5914,6805,0,18459,0}, + {{0x09,0xdb,0xf4,0xe7,0xea,0xae,0xe5,0x26,0x29,0x49,0x62,0x59,0x17,0xb7,0x3a,0xe1,0x12,0x47,0x8a,0x38},20009,7912,46,9986,6805,0,18459,0}, + {{0x0a,0x21,0x90,0x0d,0xec,0xba,0x97,0x70,0xc9,0x66,0xdb,0x03,0x58,0xc1,0xb1,0x6d,0xe1,0x39,0xc7,0x22},19776,6971,41,9652,6805,0,18459,1}, + {{0x0b,0x1e,0xac,0xf8,0x30,0xc2,0x2e,0x85,0x19,0xce,0x7c,0x8c,0x7c,0x82,0x1a,0x16,0x74,0x3e,0x44,0xed},18280,1570,67,1899,16652,0,18459,0}, + {{0x0b,0xd2,0x12,0xfa,0xe9,0x0c,0xec,0x9a,0xa6,0x4a,0xe2,0x29,0x8d,0xa3,0x3d,0xbc,0x63,0xcf,0x2a,0xdc},18720,2997,82,16480,6805,0,21138,2}, + {{0x0c,0x02,0x06,0x28,0x2e,0x52,0xce,0x25,0x31,0x9a,0xbf,0x2f,0x71,0xde,0xa6,0x1d,0x42,0xf1,0x36,0x6e},19437,13265,67,13962,16652,0,18459,2}, + {{0x0c,0x82,0xe3,0x47,0x30,0x82,0xc4,0x5a,0xd7,0x50,0x0d,0xf2,0x7c,0xb7,0x5e,0x68,0xa9,0x97,0xa8,0xd7},18239,873,72,2951,6805,0,21138,0}, + {{0x0c,0x8d,0x8c,0x95,0xb9,0x5c,0xc4,0xf3,0x6a,0xe7,0x1f,0x7b,0x68,0xe6,0x59,0x1c,0x22,0x55,0xc0,0xd7},21190,12808,77,5914,6805,21171,21138,0}, + {{0x0e,0xbc,0x33,0x1c,0x4d,0x7d,0x96,0x3f,0x4a,0x65,0x53,0xe6,0xf2,0x02,0x0f,0xa5,0xa1,0x03,0x17,0x53},21844,15770,77,5914,6805,0,20657,0}, + {{0x0e,0xdc,0x8e,0x94,0x81,0x44,0x63,0x5d,0x4b,0xb2,0xd1,0xa9,0xcf,0x07,0x75,0x54,0xed,0x53,0x73,0x34},21003,11830,77,9652,6805,0,21138,2}, + {{0x0f,0x5c,0x54,0x0f,0x56,0xfb,0x16,0x4c,0x23,0x86,0x7a,0xcb,0xf1,0x36,0xc2,0x3a,0x39,0x34,0x78,0x76},22318,18089,62,9701,6805,0,18459,0}, + {{0x0f,0x75,0x69,0xbe,0x19,0x83,0x77,0x74,0xa8,0xb4,0x9c,0x86,0x23,0x05,0x55,0xff,0xad,0x4c,0xd6,0x37},21871,15845,62,5914,6805,21862,21138,0}, + {{0x0f,0xd9,0x6d,0x24,0xad,0xd0,0x88,0xd1,0xfe,0xa9,0x3e,0x60,0x0b,0xab,0x7d,0xe7,0x24,0xd2,0x14,0x79},21012,11852,132,11866,16652,0,21138,0}, + {{0x10,0xcf,0xd9,0xc5,0x84,0x1d,0xa3,0xc5,0xbc,0xab,0x80,0x73,0xd2,0x98,0x2a,0x3f,0xe0,0xbf,0xde,0x9e},21965,16326,77,11880,6805,0,21138,0}, + {{0x11,0x0a,0x81,0xe2,0xe4,0x2c,0x1f,0xba,0x95,0x64,0xc0,0xd6,0x84,0x41,0x8a,0xd9,0x50,0x41,0xb8,0xe0},22093,17056,67,5951,16652,0,18459,0}, + {{0x11,0x8f,0x98,0x40,0x45,0x4a,0xf1,0x72,0x2d,0x79,0x04,0xd4,0xd2,0x4b,0xca,0x09,0x2f,0xfd,0x23,0x09},19404,5761,51,9986,6805,0,18459,0}, + {{0x12,0x0d,0x77,0x53,0x93,0xaa,0x14,0x35,0xa0,0x39,0x42,0xab,0x55,0x07,0x82,0x7a,0x94,0xe4,0xae,0xc8},21339,13858,77,12903,6805,0,21138,0}, + {{0x12,0x18,0x2e,0x87,0xa0,0x57,0xee,0x33,0x7a,0xe3,0xd9,0x55,0xf1,0x82,0xf2,0x4f,0x48,0x18,0x9d,0x1b},20748,10948,77,9663,6805,0,21138,0}, + {{0x12,0xfd,0xad,0xc8,0x5f,0xc1,0x2e,0x2a,0x36,0x26,0x38,0xed,0xbd,0xd8,0x7f,0x19,0x29,0xe9,0xd5,0x8d},22263,17843,51,5914,6805,22259,18459,0}, + {{0x13,0x05,0x80,0x6c,0x0a,0xda,0xa7,0x01,0xe1,0xbc,0x29,0x6b,0xe7,0x22,0x61,0x8b,0x59,0xfe,0x02,0x22},18648,3276,72,939,6805,0,21138,1}, + {{0x13,0x8a,0x59,0x9e,0x7d,0xdf,0xef,0x31,0x25,0xcf,0xf7,0x42,0x39,0xd1,0x9c,0xa0,0x9e,0x70,0x3b,0x23},18711,3069,82,16480,6805,0,21138,2}, + {{0x13,0xc0,0x23,0x48,0x0f,0xff,0xb9,0xba,0xc3,0xa8,0x82,0x34,0x3f,0xfd,0x61,0x27,0x6e,0xb5,0x5a,0xef},20085,7663,67,9986,6805,0,18459,0}, + {{0x14,0x3f,0x84,0xc1,0x8d,0x43,0xad,0xd0,0xc7,0xb6,0x4a,0xa1,0xb6,0x08,0x7e,0xb1,0xf5,0x83,0x84,0xe1},21357,8698,72,9986,6805,0,21138,0}, + {{0x14,0x5e,0x81,0x40,0x9c,0xec,0xba,0xff,0x6f,0x10,0x30,0xb9,0x9a,0x26,0x0d,0x6d,0xe4,0x10,0x77,0x42},20032,7970,62,5914,6805,0,18459,1}, + {{0x14,0xc5,0xc2,0x94,0xc5,0xa3,0xfb,0xa2,0x20,0x92,0xa0,0x1e,0x5e,0xc4,0xc8,0x51,0x2f,0x45,0x83,0xce},19075,4535,62,9652,6805,19066,21138,0}, + {{0x14,0xfb,0x3b,0x1e,0xd0,0x09,0xbc,0xbe,0x48,0x93,0x57,0x8f,0x22,0x4c,0xfd,0xb0,0xcf,0xc5,0x0b,0xc9},20679,10794,67,9986,6805,0,21138,0}, + {{0x15,0xa0,0xc0,0x3e,0xce,0xeb,0x78,0x0a,0x73,0x44,0x8b,0x86,0x52,0xd0,0x62,0x2c,0xa0,0xc4,0xc1,0xc2},20481,9815,77,10706,6805,0,21138,0}, + {{0x15,0xf8,0x2c,0x44,0xce,0xae,0xe8,0x16,0xa1,0x28,0x26,0xf6,0x80,0x94,0xe5,0xfd,0x4c,0x12,0x0d,0x10},20255,9017,67,10706,6805,0,21138,0}, + {{0x16,0x2b,0xbd,0x4b,0x20,0x92,0xaa,0x9f,0x0d,0xac,0x4d,0xae,0x9e,0xdd,0x7d,0x38,0x6a,0x28,0x55,0x3a},19635,6239,62,5914,6805,0,21138,0}, + {{0x17,0x90,0x83,0x78,0xb7,0x77,0x2f,0x9d,0x75,0x44,0xad,0x2b,0x97,0x56,0xfa,0x5f,0x16,0x51,0xc6,0x12},21162,12644,72,9947,6805,0,21138,0}, + {{0x18,0x21,0xcc,0x3f,0xee,0xb3,0xf3,0x04,0xdb,0xd1,0x02,0x85,0x5b,0x4a,0xac,0x6f,0x17,0x41,0xad,0x78},20228,8929,51,6157,6805,0,18459,0}, + {{0x18,0x70,0xec,0xb7,0xb2,0x0b,0xaa,0x51,0x12,0x75,0x80,0x92,0x17,0xfb,0x9f,0x29,0xdd,0x62,0x66,0x66},18728,3030,82,16480,6805,0,21138,2}, + {{0x18,0xb3,0xf9,0xbf,0xd2,0x0c,0x93,0x47,0x29,0x29,0x39,0x73,0x5e,0x44,0xf1,0xb9,0x63,0xf9,0xeb,0xf5},19872,12346,46,9652,6805,0,18459,0}, + {{0x1a,0x0d,0x1d,0xbe,0x9b,0x7f,0x56,0x5d,0xfd,0x5b,0xfb,0x09,0xcb,0xa0,0xf6,0xdc,0xad,0x54,0xb7,0xcc},19217,4737,62,9652,6805,0,21138,0}, + {{0x1a,0x43,0xb2,0x68,0xc5,0x1f,0xa5,0xce,0xaa,0x1a,0x32,0x88,0x72,0xa7,0xa5,0xef,0x10,0x6d,0xf0,0xfe},18324,1205,67,9168,6805,0,21138,0}, + {{0x1a,0x9e,0xdd,0xc5,0x32,0xb9,0x1b,0x68,0x60,0xb8,0x33,0x8c,0x63,0x71,0x75,0x7b,0x55,0x80,0x79,0xfb},18869,3556,67,5914,6805,18860,21138,0}, + {{0x1a,0xdf,0xc8,0x31,0xb8,0x58,0xb6,0xe9,0x23,0x08,0x7e,0x92,0xaa,0x49,0x49,0x93,0x98,0x19,0x57,0x43},21554,14457,62,9701,6805,0,21138,0}, + {{0x1b,0x0e,0xc3,0x1a,0x67,0xf0,0xce,0x25,0xdc,0x11,0x7d,0x8b,0x69,0x1d,0x3f,0x19,0xb8,0xc8,0xf6,0x83},20331,9316,62,10706,6805,0,18459,0}, + {{0x1b,0x81,0x9a,0x66,0x35,0xc7,0xd9,0x6e,0xd2,0xbe,0xdc,0x43,0xc4,0x6c,0x13,0xe4,0xaa,0x01,0xc7,0x7f},20560,10118,67,7452,6805,0,21138,1}, + {{0x1b,0xab,0x1a,0x0e,0x1b,0xb8,0x36,0xba,0xe6,0x2f,0xf1,0xd8,0x00,0xb8,0x9a,0x75,0xaa,0x9a,0x23,0x7d},22294,5266,77,5914,6805,0,21138,2}, + {{0x1b,0xd2,0xb1,0xa0,0xe6,0x09,0x70,0xc1,0xdc,0x69,0xe9,0x68,0x10,0x9b,0x03,0xb4,0x02,0x95,0x70,0x78},18212,338,72,15417,6805,0,21138,0}, + {{0x1c,0x2a,0x66,0x3e,0xfd,0x72,0x14,0xd2,0x74,0xf8,0xcd,0xd3,0x3c,0x36,0x78,0x9b,0xb0,0x3c,0x99,0x6e},20909,11414,62,752,6811,0,18459,0}, + {{0x1c,0xb2,0x40,0x0f,0xb2,0x5e,0xd6,0xc0,0x9f,0x86,0x4f,0xb9,0x0f,0x29,0x7c,0x84,0x4e,0x66,0xf7,0x66},19881,7168,62,10706,6805,0,18459,0}, + {{0x1c,0xf2,0x00,0x24,0x17,0x3f,0x0a,0xf2,0x1b,0x8a,0x37,0xc4,0x96,0xea,0xb3,0x8f,0x83,0x8b,0x77,0x39},21826,15686,72,9652,6805,0,21138,0}, + {{0x1d,0x40,0x3b,0x3e,0x77,0xca,0xcf,0xf6,0x20,0x89,0x43,0xea,0x16,0x0a,0x50,0x65,0xfb,0x9f,0x35,0x2a},19668,6607,46,9986,6805,0,18459,0}, + {{0x1d,0xfd,0x3e,0xf2,0x1d,0x76,0x5d,0x4b,0x44,0x30,0xdd,0x52,0xf5,0x8e,0xb9,0x2d,0x96,0x2d,0xfc,0x85},22155,17269,72,9701,6805,0,21138,0}, + {{0x1e,0x48,0x80,0xe0,0x18,0x59,0xc6,0x3f,0xfc,0x20,0x20,0xbb,0x2f,0x42,0x99,0xd9,0xc8,0xca,0x7d,0xbf},20396,12612,51,5914,6805,0,18459,0}, + {{0x1f,0x13,0xc7,0xac,0x13,0x2d,0x7f,0xc6,0x69,0xc1,0x38,0x2b,0xd9,0x9c,0xa1,0xbb,0x5a,0x25,0x79,0x04},18807,3239,67,9701,6805,0,21138,0}, + {{0x1f,0x24,0x83,0xc9,0x48,0x12,0x98,0x08,0x31,0xa6,0x2f,0xdf,0x6f,0xd3,0xda,0x60,0xce,0xff,0xd6,0x1f},21990,16753,51,17099,6805,0,18459,0}, + {{0x20,0x08,0xa7,0x39,0xc9,0x99,0x43,0x09,0xe7,0xcb,0x5b,0x60,0xa8,0x0e,0xf9,0x24,0x9f,0x43,0x9f,0xd4},21145,14949,46,9652,6805,0,18459,0}, + {{0x21,0x0c,0xbc,0x71,0x1f,0x82,0x0d,0xbe,0x1a,0x7e,0x1b,0xb0,0x53,0xe8,0xd1,0x1a,0x94,0xde,0xd1,0xe0},20846,11256,72,9652,6805,0,21138,0}, + {{0x21,0xe1,0x6d,0x8a,0x26,0x7a,0xe2,0x09,0xba,0x56,0x81,0x65,0x28,0xcf,0xad,0x82,0x1c,0x26,0xc5,0x87},20800,11067,62,2796,6805,0,21138,0}, + {{0x24,0x51,0xd9,0xa7,0x25,0xc3,0xc6,0xc5,0x02,0xd1,0xad,0x0f,0x46,0x9e,0x99,0xfc,0xd2,0x70,0xda,0xae},21385,13888,67,5914,6805,21376,21138,0}, + {{0x24,0x54,0xdb,0x7d,0xa7,0xb9,0x6b,0xf1,0x40,0xb4,0x9a,0xa9,0xe7,0xf3,0xb5,0xd8,0xf6,0x0b,0x00,0x39},18423,1906,72,4679,6805,0,21138,0}, + {{0x24,0x8e,0x96,0x7c,0x40,0x69,0xbf,0x04,0x7b,0x1a,0x11,0x4a,0x77,0x68,0xbb,0x21,0x10,0xf4,0x30,0xa4},21593,15136,77,9701,6805,0,20657,0}, + {{0x24,0xfe,0xef,0x69,0xcf,0x75,0x2d,0x22,0x29,0xb5,0x79,0x1c,0xc6,0x2b,0xdd,0xc7,0x55,0x54,0xe0,0xeb},18165,595,102,9648,6805,0,21138,0}, + {{0x25,0xa9,0xfb,0x58,0x99,0x7f,0x60,0x69,0xfa,0xba,0xbe,0x46,0xc4,0xe0,0x9d,0xec,0xb7,0x90,0xe6,0x29},18799,3219,51,9652,6805,0,18459,0}, + {{0x26,0x2a,0xd1,0x28,0x89,0x1d,0xd9,0x7a,0xaa,0x59,0xba,0xf2,0xf8,0x05,0xee,0x91,0x15,0xa1,0x27,0x7d},18373,1431,62,9986,6805,0,21138,0}, + {{0x26,0x33,0x26,0x66,0x1e,0xd7,0x7d,0x88,0xec,0x17,0x0d,0xdb,0xb2,0xb4,0xdc,0x7a,0xfb,0x25,0xd3,0x5d},18755,3348,77,9652,6805,0,21138,0}, + {{0x26,0x35,0x95,0x6c,0x51,0x2e,0xc0,0x8f,0x44,0xb6,0xdd,0xa8,0xa6,0xdc,0x01,0x9e,0x86,0x8e,0x10,0xe2},20193,8748,62,9644,6805,0,18459,0}, + {{0x26,0x6e,0xb1,0x2d,0x8d,0x47,0xc8,0x2a,0xd6,0x7e,0x85,0xb8,0x07,0x64,0x53,0x02,0x0e,0xed,0xcc,0x9a},19818,12296,46,17120,6805,0,18459,0}, + {{0x26,0xfb,0xa2,0xcb,0x2c,0x37,0xe7,0x8e,0x98,0x7b,0x8a,0x03,0xc9,0x14,0x0a,0x51,0x40,0xde,0xa9,0xdd},21030,11908,62,17099,6805,20058,18459,0}, + {{0x27,0x53,0xe9,0xa6,0x41,0x5b,0x1a,0xb5,0xc0,0x70,0x29,0x0d,0x44,0x8c,0x49,0x15,0x29,0x45,0x1c,0x1f},18914,3804,67,6322,6805,0,21138,0}, + {{0x27,0x98,0xbb,0x11,0x4f,0xcb,0x3b,0x2f,0x1e,0x63,0x36,0x06,0x63,0x91,0xa1,0xba,0x5a,0x0c,0xc6,0x9a},20052,8070,62,17099,6805,20047,21138,0}, + {{0x27,0xbb,0x73,0xa3,0x37,0x2a,0x01,0x89,0xad,0x95,0x97,0xfc,0x8d,0x9f,0xb5,0x46,0xcc,0x46,0x6e,0xef},18173,713,77,9652,6805,0,21138,0}, + {{0x28,0x7a,0x0f,0x98,0x9a,0x59,0xb4,0x48,0x60,0x23,0xd8,0x44,0x94,0xb8,0x62,0xe1,0x49,0xed,0x99,0x02},20211,16200,51,14830,6805,0,18459,0}, + {{0x29,0x04,0x8f,0x59,0x0d,0xfa,0x9c,0x29,0x65,0xa6,0x9a,0x3a,0x9b,0xc3,0xd6,0xc9,0x99,0xcb,0xc2,0x9f},19298,5551,51,10706,6805,0,18459,0}, + {{0x29,0x75,0x82,0x42,0xbd,0xf0,0xfa,0x0b,0x1e,0x01,0x93,0xee,0x8a,0xc7,0x78,0x1e,0xfa,0x8e,0xc3,0x0b},18594,2628,87,699,6805,0,21138,1}, + {{0x29,0x93,0xce,0xf0,0xcf,0xb9,0x97,0xc3,0x46,0x33,0xfe,0x91,0x31,0xbc,0xa0,0x89,0x35,0xd3,0xdf,0xa1},19377,5671,51,9652,6805,0,18459,0}, + {{0x2b,0x3c,0xc8,0x2c,0x63,0x78,0x94,0xd9,0x09,0x6b,0xc6,0xa5,0xc9,0xf5,0xad,0x58,0x09,0x85,0x25,0x2b},19932,6987,72,10706,6805,0,21138,1}, + {{0x2c,0x6e,0x16,0xfd,0xec,0x3a,0xaa,0x22,0xab,0x27,0x6d,0x50,0xd9,0x4d,0xb6,0x50,0x83,0x65,0xb3,0x18},20855,11303,132,426,16652,0,21138,0}, + {{0x2c,0x9b,0xe3,0x92,0x6d,0x7c,0xb5,0xe5,0xd4,0x6e,0x54,0x18,0xbe,0x8f,0xd0,0xc0,0x1d,0xeb,0x30,0x9f},22259,17878,51,4257,16652,0,18459,0}, + {{0x2d,0x30,0x57,0x8a,0x99,0xe6,0x67,0xac,0xa8,0x2b,0x20,0x22,0xb1,0xb4,0x62,0x27,0xa9,0xd8,0x71,0xce},20340,9364,46,5914,6805,0,18459,0}, + {{0x2d,0x50,0x59,0xfc,0x38,0xba,0x24,0xd9,0xee,0x43,0x4a,0x33,0xeb,0x76,0xbf,0x3d,0x3a,0x91,0x9b,0x02},19514,6118,137,9297,16652,0,21138,0}, + {{0x2d,0xa1,0xea,0x9a,0x6a,0x83,0xf7,0xc8,0xc8,0x30,0x83,0x67,0x79,0xab,0x00,0x68,0x38,0x75,0x9f,0xb9},22233,17746,62,14072,6805,0,21138,0}, + {{0x2e,0x6e,0x3d,0xee,0x64,0x07,0x53,0xc4,0x75,0x8e,0x2b,0x27,0xb6,0x32,0x2f,0xaf,0x51,0x72,0x88,0xf8},22055,16906,41,9986,6805,22048,18459,0}, + {{0x2e,0xce,0x49,0xb1,0x29,0x13,0x33,0xa7,0x46,0x0f,0xaa,0x03,0xfc,0x69,0x59,0xc3,0x01,0xf2,0xd2,0x38},21312,13727,82,267,6805,0,21138,0}, + {{0x2e,0xf1,0xab,0xf5,0x97,0xff,0x31,0x23,0x72,0x2e,0x8d,0x3f,0xe3,0x84,0x75,0x6b,0x1b,0x01,0x4e,0x96},18299,1012,67,6140,16652,0,21138,0}, + {{0x2f,0x70,0x2c,0xfb,0x44,0x08,0xef,0x5b,0xcc,0xc6,0x43,0xe5,0x93,0x74,0xf0,0x55,0x02,0x53,0xa4,0x5b},21395,13982,62,9156,6811,0,21138,0}, + {{0x31,0x3c,0x0b,0xf7,0x94,0x5d,0x0f,0x90,0xb2,0x10,0xad,0x83,0x03,0xf8,0xb5,0x1a,0x89,0xe8,0x56,0xda},21599,15184,62,9701,6805,0,21138,0}, + {{0x31,0x53,0x72,0xab,0x10,0x0e,0xc1,0x46,0x34,0xb2,0x23,0xb9,0xce,0x23,0xce,0xdf,0xcb,0xfa,0x84,0x61},20594,10586,51,2225,6805,0,18459,0}, + {{0x31,0xa9,0x8a,0x47,0x76,0x83,0xcc,0x50,0x38,0x17,0xb4,0x40,0x32,0x13,0x25,0x1d,0x4b,0x64,0xa1,0x89},19738,6858,152,1251,0,19729,21138,0}, + {{0x31,0xbd,0x94,0xf7,0x57,0x6f,0x7f,0x38,0xd8,0x50,0x53,0x60,0x35,0xaa,0x29,0xef,0x0a,0xff,0x71,0x6f},19368,5652,62,10706,6805,0,21138,0}, + {{0x32,0x09,0x4d,0x6b,0x42,0x50,0x10,0x68,0x98,0x82,0x9c,0x5e,0xee,0x01,0xde,0x5d,0x4a,0x4b,0xf7,0x8a},20076,8094,51,15405,6805,0,18459,0}, + {{0x32,0x20,0x79,0xd1,0xc9,0x31,0xe3,0x5d,0x85,0xc3,0x23,0x47,0x91,0xd0,0x4e,0x22,0x09,0x1e,0x5a,0xa0},20542,10000,46,9701,6805,0,18459,0}, + {{0x32,0x31,0x0a,0xa4,0x9e,0xed,0x1c,0x6c,0xdf,0x12,0x19,0x60,0x64,0x46,0xbb,0x6d,0x0e,0xad,0x7b,0xcd},20455,9592,137,4665,16652,0,21138,0}, + {{0x33,0x96,0x37,0x09,0xf2,0xfa,0x43,0xc0,0x5c,0x7a,0x71,0x96,0xe2,0x06,0x0c,0x9f,0x53,0x36,0xfc,0x83},21750,15319,67,11880,6805,0,21138,0}, + {{0x34,0x4f,0xe3,0x1a,0x83,0x46,0x3d,0x10,0x6f,0xc8,0xca,0x1b,0x27,0x4e,0x6f,0xf0,0x76,0x49,0xf0,0x8b},20421,9474,62,9644,6805,0,21138,0}, + {{0x34,0x5a,0x83,0x39,0xb2,0xc8,0xad,0xfb,0xcd,0x05,0x55,0x13,0xa6,0x2c,0x3a,0x4e,0x8a,0xe8,0xe7,0x5a},21492,14181,67,9652,6805,0,21138,0}, + {{0x35,0x5d,0xa6,0xd2,0x7f,0x23,0xa2,0xa8,0x1d,0x29,0x64,0x41,0x22,0x99,0x31,0x0d,0xb3,0xed,0x86,0x89},20612,1802,72,4897,6805,0,20670,2}, + {{0x35,0xc0,0x1f,0x0f,0x2b,0x63,0xe7,0xfb,0x72,0xe0,0x89,0x67,0x63,0x72,0x58,0x0a,0x31,0x4e,0x85,0xd8},18248,898,72,5914,6805,0,21138,0}, + {{0x35,0xe4,0x4e,0x73,0x1c,0x70,0x69,0x5c,0x9d,0xed,0x9a,0x94,0xc3,0x9e,0x24,0x78,0xb3,0xc8,0x1e,0xe4},19950,7554,67,9644,6805,0,21138,0}, + {{0x36,0x5b,0x60,0xa0,0xec,0x27,0xdb,0xe1,0xf3,0x32,0xd0,0xac,0x80,0xe8,0xcb,0x71,0xcc,0x78,0xf8,0xfc},19784,7039,41,17099,6805,0,18459,0}, + {{0x36,0xde,0xa1,0x31,0xd1,0x72,0x1d,0xa1,0xd8,0x16,0xe4,0x0c,0x82,0x47,0x78,0x69,0x8e,0x35,0x42,0x89},19995,8871,51,7457,6805,0,18459,0}, + {{0x36,0xec,0x3d,0x80,0x05,0x32,0x17,0x2a,0x6c,0xa7,0x89,0x72,0x11,0x2b,0xd6,0xa6,0x87,0xa3,0x20,0xcc},20176,8405,46,17099,6805,20159,18459,0}, + {{0x37,0x26,0xe6,0xcf,0x1e,0x03,0x03,0xcd,0x3f,0x0c,0x19,0x68,0x38,0xa9,0x04,0xda,0xb5,0x78,0xfd,0x23},18480,1925,67,6304,6805,0,21138,0}, + {{0x37,0xa9,0x82,0xcb,0x7d,0xc8,0x73,0x60,0x84,0xd2,0x51,0xe3,0xdd,0xb6,0x6b,0x9e,0x4a,0xea,0xe8,0xe3},21048,11982,41,9644,6805,0,18459,0}, + {{0x39,0x1c,0xe8,0xde,0x33,0xda,0xd9,0x36,0x88,0xb5,0x43,0x9a,0x8a,0xf7,0xfd,0x01,0x95,0xdc,0x9b,0xcc},21200,12869,51,6304,6805,0,18459,0}, + {{0x39,0xb5,0x24,0xdb,0xd0,0x1d,0x1a,0x4a,0x61,0xf0,0x59,0x8b,0x68,0x67,0x57,0xe6,0xd3,0x4c,0x21,0x76},18661,2837,46,9986,6805,0,18459,0}, + {{0x39,0xe7,0xfe,0xa1,0xe1,0x84,0xb8,0x91,0x71,0xf9,0x2b,0x2c,0xed,0x2f,0xf1,0x53,0x42,0x29,0x14,0xc6},20264,9060,67,9663,6805,0,21138,0}, + {{0x3a,0x98,0x8e,0x3f,0xfe,0xae,0x46,0x76,0x93,0x79,0x6d,0x2f,0x9c,0xf7,0xd5,0x88,0x17,0xbb,0xc0,0x31},19289,4719,72,946,6805,0,21138,0}, + {{0x3a,0x9f,0xfa,0x55,0x3b,0x24,0x10,0x47,0xae,0x5d,0xbb,0x2f,0xf8,0xf2,0x49,0x1d,0x8a,0x0f,0x1a,0xb6},20576,10629,62,2225,6805,0,21138,0}, + {{0x3a,0xed,0x61,0x4f,0x6c,0xe9,0x06,0x07,0x0a,0x35,0xa4,0x54,0xc1,0x4e,0x59,0xe9,0x8a,0xb7,0x72,0x9a},22033,16852,46,5914,6805,0,18459,0}, + {{0x3b,0x8d,0xe4,0xf1,0xd4,0x59,0xda,0x98,0x01,0xa8,0xc3,0x0f,0xd5,0xf6,0x71,0xad,0xe7,0xfd,0x2f,0x87},21775,15455,67,5957,6805,21767,21138,0}, + {{0x3b,0x94,0x68,0x23,0x0c,0xb1,0x58,0x41,0x07,0x07,0x44,0x39,0x6b,0x4f,0x3d,0x63,0xaa,0x1c,0xac,0x07},20403,9409,72,9652,6805,0,21138,0}, + {{0x3c,0x6c,0x9c,0xf7,0x13,0x4a,0x20,0xc3,0x6c,0x19,0xdf,0xcb,0x6b,0x10,0xc3,0x2d,0x4a,0x05,0x95,0xf7},21937,16437,72,7471,6805,21918,21138,0}, + {{0x3d,0x43,0xa9,0xd5,0x9e,0x3a,0x89,0x45,0x80,0xa4,0xbb,0xab,0xc8,0x5e,0xe0,0x75,0x7b,0x09,0xd8,0x4a},19182,5017,72,5914,6805,0,20657,0}, + {{0x3d,0x55,0x94,0x3b,0x6a,0x22,0x41,0xbc,0xcc,0x49,0xd4,0x3a,0x3d,0x1a,0x04,0x4e,0xd1,0xb7,0x43,0x7f},22164,17293,62,9701,6805,0,21138,0}, + {{0x3e,0xe9,0xdd,0x98,0xaa,0x62,0xda,0xc6,0x38,0xdc,0x20,0xf2,0x2f,0x67,0x02,0x91,0x20,0xa1,0xd6,0xd6},20149,8218,72,9663,6805,20140,20657,0}, + {{0x3f,0x3b,0x4e,0x7c,0x5c,0x2a,0xe0,0xfa,0xab,0xf1,0x39,0xeb,0x95,0xe5,0xe6,0x00,0x21,0x62,0x92,0x8b},21580,15044,72,9701,6805,0,20657,0}, + {{0x3f,0xc1,0xcc,0xec,0x5a,0x31,0x37,0x73,0xbc,0x88,0x44,0x86,0xcd,0xf9,0xa5,0x4c,0x29,0xab,0xb1,0xc1},22277,5158,62,5914,6805,0,21138,0}, + {{0x3f,0xc6,0xbf,0xf6,0x6d,0x6b,0x93,0xb6,0xf4,0x1f,0x74,0xd2,0x6b,0x38,0xa8,0xdd,0xc3,0xa5,0x2b,0xba},18772,3610,62,17099,6805,18764,21138,0}, + {{0x3f,0xf3,0xe7,0xd6,0x78,0x85,0xb0,0x94,0xd1,0x49,0x54,0xff,0x07,0x84,0x69,0x5a,0x5d,0xf4,0xcb,0x11},19271,5575,72,9663,6805,0,21138,0}, + {{0x40,0x36,0x16,0x5a,0x03,0xb9,0xeb,0xf9,0xa1,0x50,0xaf,0xff,0x43,0x7b,0x5e,0x92,0x99,0x6f,0xcb,0x78},18737,3381,62,9652,6805,0,21138,0}, + {{0x40,0x4a,0x5a,0x8b,0x73,0xad,0x1b,0x77,0x71,0x4c,0xb6,0xf6,0x5b,0x20,0xbe,0x22,0x5a,0xe1,0x83,0xff},19413,5732,72,4897,6805,0,20670,2}, + {{0x40,0x6c,0x22,0x2c,0x6b,0x16,0xaf,0x09,0x7e,0x08,0x81,0x28,0xe0,0x3c,0x06,0x5f,0x22,0x7a,0x15,0x00},21956,16611,72,17099,6805,0,21138,1}, + {{0x41,0x07,0x03,0x95,0xf8,0x0d,0x59,0x60,0x18,0x84,0x01,0x9d,0xc0,0x80,0xd7,0x6a,0x11,0x00,0xba,0x08},20927,11564,62,14830,6805,0,21138,0}, + {{0x41,0xda,0x84,0x13,0x9e,0x47,0x1a,0x0f,0x5c,0x74,0xec,0xf0,0x27,0xa7,0x28,0xbc,0xc7,0x4d,0x2a,0xe6},21723,14994,62,7452,6805,0,21138,0}, + {{0x42,0xf4,0x90,0xa1,0xee,0xc3,0xd7,0x20,0x0d,0x89,0xb0,0x06,0x3e,0x40,0xb9,0xe4,0x9a,0x7d,0x5a,0x7c},22017,16777,46,17099,6805,0,18459,0}, + {{0x43,0xfb,0x5b,0x21,0x6d,0xb3,0xd8,0xac,0x38,0x7b,0x07,0xec,0x50,0x54,0x16,0x94,0xa7,0xa5,0x40,0x37},20039,8017,72,5914,6805,0,21138,0}, + {{0x44,0x34,0x83,0xa7,0x59,0x92,0x84,0xc7,0xd4,0xce,0x22,0x84,0x64,0xc7,0x92,0xdb,0xc2,0x2b,0xad,0xa0},20047,8083,62,8168,16652,0,21138,0}, + {{0x45,0x3e,0xef,0x88,0x7f,0x04,0xad,0x98,0x30,0xf3,0x4c,0xde,0x40,0xba,0xa3,0xfa,0xeb,0x42,0xb4,0xd4},18196,758,67,10706,6805,0,21138,0}, + {{0x45,0x74,0xc1,0x57,0x66,0x85,0x85,0x12,0xe0,0x3c,0x16,0x10,0xa6,0x7d,0x3f,0x48,0xd5,0x04,0xaa,0x1e},18497,2149,41,5914,6805,0,18459,0}, + {{0x45,0xc5,0x57,0xd0,0x39,0x47,0xa9,0x39,0x49,0x5c,0x00,0x1d,0x10,0x0a,0x2d,0x3a,0x32,0x5b,0xef,0xed},21086,1158,72,946,6805,0,21138,0}, + {{0x46,0x2a,0x43,0xd0,0x92,0x5b,0x44,0x22,0xad,0xf8,0x4b,0x12,0x71,0x51,0xa1,0x7b,0xc3,0x7b,0x2a,0x0a},20985,11613,67,9652,6805,0,18459,0}, + {{0x46,0xcd,0x07,0xcd,0x5f,0x07,0xa3,0xa2,0x64,0x56,0x13,0xd2,0xc5,0xb0,0xac,0x36,0x67,0xdf,0x38,0x92},19020,4350,67,9701,6805,0,21138,2}, + {{0x48,0x02,0x21,0xe8,0x74,0xd2,0xe3,0x68,0x95,0x54,0xd6,0xe8,0x01,0x75,0x65,0x34,0x3e,0x10,0xf6,0x9e},22048,16891,46,15641,16652,0,18459,0}, + {{0x48,0x5b,0xd5,0x61,0xf0,0x72,0x5f,0x85,0x2d,0xfe,0x76,0x2b,0xed,0x01,0xf7,0x0c,0x93,0xb9,0xe3,0xd9},18349,1268,67,5914,6805,0,21138,0}, + {{0x48,0x60,0x45,0x92,0x29,0xe3,0x1d,0xbb,0x66,0xcf,0xfb,0x1f,0xe2,0x70,0x51,0xc5,0xbe,0x35,0x6c,0x0a},21616,14526,62,9701,6805,0,18459,0}, + {{0x48,0xd2,0x33,0x07,0x05,0xfc,0x75,0x96,0xf5,0x96,0x00,0x95,0xc9,0x94,0xca,0x7d,0xea,0x1a,0xb0,0xb0},19360,5625,97,3901,16652,0,18459,0}, + {{0x49,0x20,0xb8,0x7a,0x8e,0xe8,0x1a,0xba,0xfc,0xdf,0x56,0x8f,0xfa,0x3a,0x67,0x61,0xd7,0xc9,0x7a,0xc8},18905,3739,72,691,6805,0,20657,0}, + {{0x4c,0xaa,0x8f,0xdf,0xbb,0x83,0x9c,0x16,0x89,0x82,0xf5,0xeb,0x5f,0x35,0xf7,0xb4,0xbf,0xa7,0x3c,0xed},21810,15611,87,9663,6805,0,21138,0}, + {{0x4c,0xc5,0x70,0x65,0xbd,0x83,0x3a,0x46,0xb5,0xc5,0xd4,0xb3,0xc3,0x8f,0xaf,0x8a,0x32,0xab,0xc6,0x55},20837,11277,82,9684,6805,0,21138,0}, + {{0x4c,0xe0,0x06,0x44,0xe4,0x9b,0x7e,0x0e,0x7b,0xbb,0xd9,0xc7,0x3b,0xff,0x50,0x00,0x68,0xf9,0xaa,0x30},20282,9084,112,9289,16652,20273,21138,0}, + {{0x4c,0xe6,0x8c,0xc3,0xb8,0x65,0x17,0x41,0xdf,0xb2,0xf3,0x00,0xa4,0x8d,0x8b,0xab,0xb8,0x1a,0x67,0x42},19845,12093,41,9652,6805,0,18459,0}, + {{0x4d,0xd7,0x26,0xcf,0xd7,0xdc,0x6a,0x4e,0x1d,0x2b,0x05,0xe0,0xd4,0x2d,0x89,0xe3,0x1f,0x2e,0x6a,0xa2},21732,15287,62,7457,6805,0,21138,0}, + {{0x4e,0x68,0x12,0xae,0x03,0x0e,0xb1,0xb0,0x9e,0xf5,0x15,0xba,0xa6,0x5d,0xce,0xe2,0x61,0x3e,0xe8,0xed},19619,6202,62,6322,6805,0,21138,0}, + {{0x4e,0x74,0x2f,0x76,0x96,0x8b,0x20,0xd1,0x84,0x2e,0xa4,0xcb,0xa9,0xcd,0x9e,0x02,0x31,0x59,0x8e,0x17},18693,2905,46,9986,6805,0,18459,0}, + {{0x4f,0x59,0x32,0xd1,0x3d,0x5c,0x17,0x8c,0xfb,0xc0,0x66,0xe2,0x5f,0x5c,0xf5,0x35,0x58,0xce,0x80,0x23},20809,11115,62,2796,6805,20800,21138,2}, + {{0x4f,0xae,0x26,0x90,0x24,0x1f,0x81,0x98,0x30,0xfa,0xcb,0x30,0xfa,0x7c,0x47,0x35,0xfd,0xc7,0x11,0x3f},21449,14097,72,6322,6805,0,21138,0}, + {{0x4f,0xda,0x5a,0xd3,0x61,0x3d,0x05,0x69,0x4d,0x7d,0x2c,0x0c,0x1d,0x30,0xa8,0xec,0x3e,0x65,0x4a,0x0f},18852,3513,62,4257,16652,0,21138,0}, + {{0x50,0x1a,0x11,0x3e,0xe8,0xec,0xb5,0x58,0x0d,0xce,0x1a,0x91,0x49,0xe7,0xd7,0x1a,0x8b,0xf4,0x63,0x91},18939,3868,72,9663,6805,18930,21138,0}, + {{0x50,0x39,0x65,0xf6,0x89,0xe0,0x12,0xa1,0x28,0x3a,0x13,0x37,0xff,0x05,0x9c,0x4a,0xf4,0xa9,0x43,0x10},18879,16120,67,17167,16652,0,21138,0}, + {{0x50,0x87,0x8c,0x5d,0x63,0xb2,0x5b,0x96,0xab,0x73,0xf9,0x77,0x49,0x13,0xdb,0x15,0x6b,0x1a,0xe1,0x39},21669,14691,51,9986,6805,0,18459,0}, + {{0x50,0xc2,0xa6,0x8d,0x3b,0x1a,0xb9,0x98,0x02,0xec,0x86,0x5a,0xf9,0x01,0xe8,0x45,0x4d,0xf6,0x2f,0xc4},21067,12455,72,5914,6805,0,20657,0}, + {{0x50,0xc3,0x76,0xdd,0xcc,0x73,0x84,0x28,0xd1,0x97,0x1a,0x22,0x08,0xee,0xc2,0x13,0xa5,0xf3,0x31,0xc4},20273,9108,132,9289,16652,0,21138,0}, + {{0x51,0x00,0xdb,0x1a,0xbb,0x77,0x45,0x95,0x08,0xcf,0xbe,0xb4,0x3a,0x86,0x92,0x0c,0x0e,0x91,0x3a,0xcf},262,240,62,5933,16652,0,21138,1}, + {{0x52,0x40,0x96,0x54,0x2b,0xf3,0xdd,0x25,0x17,0x87,0xfc,0xc7,0x81,0x8b,0x62,0xa5,0x3a,0xd8,0xbe,0x3b},21062,12427,67,17099,6805,0,21138,0}, + {{0x52,0x58,0x76,0x64,0xe6,0xc1,0x6c,0x5b,0xb8,0x27,0x87,0xea,0x04,0x40,0x69,0x5e,0xe6,0x39,0x7a,0x8e},20819,11175,67,2796,6805,0,21138,0}, + {{0x52,0x70,0x2f,0x07,0x6c,0x14,0x3d,0x1d,0x3e,0x8d,0x63,0x03,0x19,0xad,0xa5,0x38,0x84,0x05,0xc7,0xe1},21802,15557,62,8969,6811,0,21138,0}, + {{0x52,0xdb,0x76,0x82,0x3e,0xa5,0x7c,0x4e,0xf6,0xcd,0x1e,0x9b,0x1c,0xd1,0xa3,0xc4,0x70,0x46,0x98,0xf7},20730,10900,51,11942,6805,20722,21138,0}, + {{0x53,0x2a,0xe0,0xb0,0x2f,0xdd,0xef,0x06,0x0c,0xc4,0x50,0x0f,0x5a,0xf2,0x6c,0xd9,0x30,0x8b,0x73,0x3b},19243,5460,72,16346,6805,19234,21138,0}, + {{0x53,0x47,0x3d,0x2e,0xb1,0x3f,0xb9,0x1b,0xcd,0x96,0x6f,0x12,0x53,0xc7,0x74,0x85,0x63,0xe7,0xbc,0x92},19158,4838,62,15520,6805,0,18459,0}, + {{0x53,0xb2,0xda,0xb8,0x07,0x2c,0x23,0x41,0x68,0x71,0xbe,0x71,0x61,0x35,0xa0,0xcd,0xcf,0xa9,0x5f,0x16},20219,8902,51,9144,6805,0,18459,0}, + {{0x54,0x47,0x92,0xf0,0xd6,0x09,0xf2,0x0c,0xa9,0xe7,0xb4,0xd3,0xf5,0xf0,0xbc,0x29,0x5c,0x2e,0x9c,0xa9},22288,5212,62,5914,6805,22277,21138,0}, + {{0x55,0x55,0xf1,0xeb,0x3b,0x21,0x3b,0x71,0x0e,0x19,0xa6,0x2c,0x66,0xc9,0x5b,0xde,0xd1,0x74,0x68,0xec},19280,5515,62,7471,6805,0,21138,0}, + {{0x55,0x73,0x0f,0xca,0x43,0x66,0x7b,0x79,0xca,0x69,0xa2,0x20,0x92,0xdd,0xeb,0x9e,0x47,0xf8,0xea,0xc4},21661,14660,72,9652,6805,0,20657,0}, + {{0x56,0x9e,0x93,0x75,0xfa,0xa5,0x86,0x96,0xb7,0xeb,0x75,0x93,0x28,0xcd,0x64,0xcb,0x91,0x19,0xe1,0xdc},19505,6063,41,9652,6805,0,18459,1}, + {{0x56,0xc1,0x4e,0x1a,0x17,0xd1,0xc4,0x55,0xd5,0xca,0x5f,0xb7,0x07,0x26,0x2e,0x79,0x70,0xe8,0xef,0xa5},20891,11386,67,5914,6805,0,18459,0}, + {{0x56,0xef,0x1e,0xcd,0xb4,0xc1,0x3a,0xae,0x23,0x02,0x86,0xd1,0x48,0xc4,0x38,0x8e,0x87,0xbe,0x21,0x6b},20312,9246,67,9913,16652,0,21138,0}, + {{0x57,0x47,0x23,0x6d,0x03,0x7d,0x3e,0x7d,0x93,0x1e,0x6c,0x68,0x29,0xb0,0xfa,0xf9,0x8e,0xdc,0x9f,0x47},22200,7272,77,9973,6805,0,21138,0}, + {{0x58,0x43,0xc7,0xad,0x36,0xe6,0x48,0x46,0x83,0x40,0x8f,0x0a,0x51,0xe8,0x79,0xd7,0xea,0xca,0xe3,0xe1},20828,11224,62,9986,6805,0,21138,0}, + {{0x58,0x94,0x4f,0xc7,0xc2,0xf2,0xa1,0xda,0x65,0x28,0xe9,0x5b,0x46,0xd7,0x3a,0xde,0xad,0x35,0x5e,0x71},18702,2959,72,16480,6805,0,21138,0}, + {{0x58,0xbc,0x6a,0xf8,0xae,0x5b,0xca,0x7b,0xd2,0x72,0x6a,0xfe,0x04,0xc8,0xf1,0xec,0x19,0xe8,0xd2,0x4b},19985,7867,46,1393,6805,19976,18459,0}, + {{0x5a,0xa2,0xce,0x24,0xe9,0x3b,0x02,0x50,0x06,0x9a,0x43,0xd9,0x9f,0x1b,0x9b,0xe9,0x30,0x6b,0x7c,0x25},21465,9762,62,9701,6805,0,21138,0}, + {{0x5a,0xcd,0xd7,0x41,0xb0,0x6c,0x40,0x44,0x69,0x4d,0xfe,0xb1,0xb7,0xd8,0x4a,0xbd,0xb6,0x91,0x49,0x4d},19487,6007,77,5914,6805,0,21138,0}, + {{0x5c,0x22,0x5a,0x59,0xb4,0x9a,0x08,0xf8,0xeb,0x7d,0xd5,0x19,0x37,0x90,0x0d,0x18,0x84,0xf4,0x46,0x40},20202,8802,67,9644,6805,0,21138,0}, + {{0x5c,0xa6,0xce,0x8f,0xd1,0xea,0x7a,0x76,0x69,0xe3,0xe1,0xa0,0xa0,0x81,0x54,0x97,0x6f,0x2f,0x9d,0x6f},22033,16852,46,5914,6805,0,18459,0}, + {{0x5c,0xba,0x0b,0x94,0xb6,0x97,0xdc,0x9f,0x9f,0x45,0x7a,0x1f,0xeb,0x09,0x8f,0xd1,0xec,0xbe,0xb0,0xde},19117,4395,36,5914,6805,19109,18459,0}, + {{0x5d,0x80,0xe4,0xb3,0xb4,0x00,0x2d,0xac,0xd0,0x6e,0xdb,0xbd,0xdc,0xe5,0x5d,0x4a,0xe2,0x0b,0x8e,0x16},21972,16704,46,17099,6805,0,18459,0}, + {{0x5d,0x81,0xcf,0x1a,0xba,0x41,0x18,0x01,0xb3,0x76,0x55,0x10,0xbf,0x84,0x6d,0xab,0x74,0xfc,0x74,0xc3},21519,14329,56,9652,6805,0,18459,0}, + {{0x5f,0x08,0x89,0x52,0xd1,0x62,0xeb,0xc3,0xe7,0x2c,0x72,0x84,0x6f,0xe2,0x72,0xb1,0x03,0xad,0xd9,0x05},18135,7130,72,6304,6805,0,21138,1}, + {{0x5f,0x26,0x4e,0x9c,0xb0,0x1c,0x63,0xdc,0x34,0xfd,0x72,0x63,0x4a,0xd3,0xd8,0xfb,0x8c,0x0e,0xc0,0x60},21130,14883,51,5914,6805,0,21138,0}, + {{0x5f,0xe3,0xb4,0x77,0x28,0x6a,0x60,0xe4,0x7c,0x36,0x7f,0x8d,0xd4,0xd6,0x62,0x55,0x7a,0x62,0x58,0x8b},21121,5120,77,5914,6805,0,20657,0}, + {{0x61,0x94,0x34,0x31,0x34,0xfe,0x1d,0xd7,0x4d,0x84,0x03,0x81,0x70,0x82,0xc0,0xe8,0x7c,0x03,0xb6,0xd1},20688,10820,51,5914,6811,18280,18459,0}, + {{0x62,0x0c,0x24,0xf7,0x51,0xec,0xff,0x52,0xe3,0x36,0x3e,0x30,0xd4,0xdb,0xa8,0xb1,0xc3,0x75,0x57,0xeb},20569,10056,36,5914,6805,0,18459,0}, + {{0x63,0x43,0x6e,0xae,0x82,0x53,0x6a,0x46,0x0f,0x76,0x34,0x32,0x31,0x70,0x44,0xde,0x10,0xcc,0x3b,0xb5},18986,4073,62,9986,6805,0,21138,1}, + {{0x63,0x47,0x12,0x84,0xc5,0xda,0x06,0x1e,0x2a,0x67,0xd8,0xd5,0x47,0x22,0x55,0x13,0x26,0x5c,0x25,0x97},22306,18035,51,15520,6805,0,18459,0}, + {{0x64,0x08,0x41,0xe7,0xda,0x66,0xd6,0xf7,0x32,0x8d,0x58,0xaa,0xce,0xca,0xeb,0xcf,0x13,0x5a,0xc2,0x1b},21287,13526,67,752,6805,0,21138,0}, + {{0x65,0x1c,0xa2,0xae,0x55,0xa7,0x9f,0x72,0xf2,0x92,0x7f,0xa0,0xf0,0x43,0xd3,0x9d,0x83,0xc7,0x4d,0xe7},18114,368,62,5914,6811,0,21138,0}, + {{0x65,0x34,0x47,0x19,0xbb,0x02,0xfe,0x8d,0xfc,0x5b,0x1f,0x72,0x03,0x4a,0x01,0x1a,0x54,0x14,0xc4,0x12},19604,1083,77,4897,6805,0,20670,2}, + {{0x66,0x2b,0x27,0x55,0xa4,0x12,0x17,0xca,0xde,0x5d,0x8c,0xb1,0xea,0x54,0x67,0x40,0x06,0x88,0xfe,0x09},20873,11339,51,9652,6805,0,18459,0}, + {{0x66,0x5f,0x10,0xc0,0x1a,0x17,0x0e,0x1d,0xe0,0xf7,0xdb,0x9b,0x3f,0xe6,0x29,0x5e,0x5b,0xca,0x86,0x48},18226,812,62,9986,6805,0,21138,0}, + {{0x66,0xca,0x24,0x33,0xb5,0x6e,0xe4,0xf3,0x8c,0x81,0xba,0xa7,0xa7,0x50,0x38,0x2c,0xe5,0x4b,0x13,0x42},19586,10500,72,13847,6805,0,21138,0}, + {{0x66,0xee,0x76,0x12,0xd6,0xe8,0x17,0xa2,0x77,0x77,0x8e,0x77,0xb5,0xda,0x2e,0xc4,0xc3,0xc7,0xf5,0x06},20523,6491,46,329,16652,0,18459,0}, + {{0x67,0x10,0x2d,0xdd,0xf6,0x4e,0xc7,0xb1,0xb5,0xbb,0x77,0x61,0x27,0x38,0xa3,0xf2,0xc9,0x1c,0x66,0xff},18266,956,72,9652,6805,0,21138,0}, + {{0x67,0xe7,0xc5,0x59,0xc1,0xbe,0x20,0x34,0x50,0x2c,0xb6,0x3e,0xfe,0xa6,0x88,0x1f,0xcc,0x6b,0x8e,0x5a},22128,17381,72,5914,6811,0,20657,0}, + {{0x68,0x1b,0x70,0x4b,0x79,0x87,0x0a,0x6f,0xdf,0x69,0x18,0x71,0x8a,0x2f,0x46,0x40,0x07,0x9e,0x6f,0xc1},19809,12211,46,17120,6805,0,18459,0}, + {{0x68,0x2d,0xc0,0xc1,0x82,0x32,0xac,0xb5,0xa5,0x99,0x81,0x30,0x37,0xff,0x21,0xcb,0x0b,0xfe,0x48,0x15},20882,11362,72,9652,6805,0,21138,0}, + {{0x68,0x69,0x5c,0xf1,0x21,0xe9,0x3d,0xe1,0xf4,0x15,0x94,0xf1,0x4b,0xeb,0xb8,0x0f,0x8d,0x95,0x3c,0xf9},20126,6937,67,3829,16652,0,21138,2}, + {{0x68,0x75,0x33,0xa2,0x25,0x53,0xdd,0xf9,0x4e,0x10,0x24,0x4c,0xb9,0x2b,0xc2,0xf8,0x2c,0x25,0x7f,0x0b},27,1,67,292,6805,0,21138,0}, + {{0x69,0x8b,0x7d,0x5b,0x4a,0xb0,0x37,0xe9,0x27,0xa5,0xc6,0xb7,0x69,0xb0,0xab,0x61,0x8d,0xf7,0x3e,0x00},21643,14567,67,11759,6805,0,21138,0}, + {{0x6a,0x2b,0x06,0x33,0x93,0x5f,0x42,0x1c,0x16,0xfb,0x77,0x19,0x25,0xca,0x59,0x7d,0xb4,0xb8,0x50,0x91},22137,17222,67,5914,6805,20067,21138,0}, + {{0x6a,0xa2,0xc9,0x91,0x49,0x6a,0x68,0x30,0x2d,0xaf,0x29,0x7f,0x23,0xb9,0x68,0x19,0xd6,0x3e,0xbe,0xfa},20756,10973,77,9663,6805,0,20657,0}, + {{0x6b,0x0c,0x59,0x25,0xf8,0x9a,0x2c,0x54,0x13,0xe4,0xcb,0x36,0xae,0x81,0xbf,0x8a,0x5e,0xe5,0x4a,0x07},19334,1713,72,4897,6805,0,20670,2}, + {{0x6b,0xb9,0x15,0x09,0xc4,0x8e,0xbc,0xef,0xe7,0x02,0x57,0xc1,0xfb,0xb3,0x18,0x2f,0xb4,0x07,0xf2,0x99},20303,9177,62,9652,6805,0,18459,0}, + {{0x6c,0x7c,0x52,0x2e,0x8e,0xd8,0xdc,0x03,0x9a,0x0c,0x9d,0x18,0x51,0x2a,0x88,0x54,0x26,0xaf,0xc1,0xc5},19710,6406,46,5914,6805,19701,18459,0}, + {{0x6c,0x86,0x25,0x55,0x54,0x5f,0x26,0x6a,0xc2,0x88,0x83,0x6a,0xab,0x7f,0x9d,0xa1,0xbd,0xd4,0xe6,0xf5},21112,12558,67,7452,6805,0,21138,1}, + {{0x6c,0xe6,0x56,0x1a,0x7f,0x6b,0xf2,0xc2,0x50,0x8e,0x1c,0x1c,0x44,0x2f,0xc3,0x81,0x0a,0xd7,0xdb,0x71},20375,8509,77,9701,6805,0,20657,0}, + {{0x6d,0x4e,0x3c,0xb1,0x13,0xda,0xcf,0x15,0x8d,0x93,0x92,0xa1,0x8d,0xaa,0x41,0x3c,0x0f,0x5a,0x03,0x60},21862,15815,41,5914,6805,0,18459,0}, + {{0x6d,0xe5,0x53,0x4c,0x9c,0x3a,0xfb,0x59,0x9b,0x77,0xd1,0xd7,0xa9,0x72,0xf7,0x47,0x9b,0xe2,0xf8,0xf5},20016,7927,62,14822,6805,0,21138,0}, + {{0x6e,0x04,0x5d,0xee,0x47,0xba,0xe8,0x76,0x63,0xc6,0x09,0x4c,0x6f,0x2b,0xb6,0xff,0x3c,0x4d,0x76,0xb8},19701,6375,51,11927,16652,0,18459,0}, + {{0x6e,0x31,0x5d,0x57,0x77,0xfb,0x5d,0x43,0x67,0x5a,0xa1,0x27,0x62,0x82,0x7a,0xdd,0xfa,0xd7,0x8f,0x76},19967,7615,67,5957,6805,19959,21138,0}, + {{0x6e,0x82,0xb7,0x82,0xbf,0x38,0x24,0x1e,0xda,0x0c,0x26,0xb8,0x3c,0x90,0x5c,0x82,0x3d,0x66,0xe9,0xcf},20630,10720,72,10706,6805,0,21138,0}, + {{0x6e,0xe7,0x85,0x6c,0xc9,0xcc,0x85,0x73,0x86,0x49,0x1a,0xef,0x1d,0xcb,0xca,0x4c,0xd5,0xf2,0x8f,0x55},21269,13474,77,267,6805,0,21138,0}, + {{0x70,0x24,0xf7,0x0d,0x85,0x7e,0x11,0xa1,0x1a,0x53,0xac,0x76,0x33,0x5b,0xf4,0x66,0x71,0x81,0x1a,0x90},21890,15922,51,9986,6805,0,21138,0}, + {{0x71,0x38,0xea,0xc0,0x99,0x9e,0xba,0x52,0xf0,0x0f,0x91,0xdd,0xe9,0x40,0x00,0x6d,0x87,0xab,0x03,0x23},18534,2485,72,9986,6805,0,21138,1}, + {{0x72,0x56,0x41,0x09,0x1b,0xcc,0x13,0x81,0x29,0x8f,0x20,0x12,0x6e,0x22,0xfd,0x2d,0x89,0xee,0x41,0x01},21294,13625,77,4679,6805,0,21138,0}, + {{0x72,0xd8,0x36,0x15,0xd4,0x30,0x19,0xcd,0x7d,0xd4,0x13,0xaf,0xa4,0x57,0xd5,0x25,0x89,0x3a,0xfe,0x2f},21767,15421,67,5957,6805,0,21138,0}, + {{0x72,0xfb,0x99,0x51,0xa0,0xef,0xef,0xdc,0xa2,0xb6,0x36,0x07,0x3f,0x47,0xb2,0x27,0x51,0x29,0xde,0x72},18409,1861,72,9652,6805,0,21138,0}, + {{0x72,0xff,0xbf,0x7c,0x41,0x18,0xf7,0xba,0xf1,0x83,0x16,0x28,0x4f,0x94,0xc9,0x53,0x35,0xe4,0xba,0xc0},20639,10762,62,9701,6805,0,21138,0}, + {{0x74,0x5b,0x55,0xb4,0x33,0xe8,0xdc,0x7c,0x6c,0x58,0x30,0x08,0x7d,0xb8,0xe8,0x13,0x35,0x19,0xec,0xd0},22248,17824,46,4257,16652,0,18459,0}, + {{0x74,0xaa,0xf1,0x58,0x41,0xf8,0x02,0x43,0x94,0x49,0x5f,0x37,0xfd,0xae,0xe0,0x36,0xfd,0x3d,0x22,0x3c},20704,10842,62,9986,6805,0,21138,0}, + {{0x75,0x40,0xdd,0xae,0x6b,0x0f,0xd2,0x39,0xfd,0x52,0xfa,0xc2,0x60,0xad,0xe8,0xfb,0x1c,0x57,0xf4,0xcc},18679,2884,72,5914,6805,0,21138,0}, + {{0x75,0x7a,0xe6,0x88,0xd5,0xd2,0xe5,0xe4,0x6c,0xe4,0xa3,0x29,0x3e,0xc8,0xd1,0x3a,0xe0,0x3f,0x77,0x2a},18896,3688,51,1393,6805,0,18459,0}, + {{0x76,0x05,0xc6,0x20,0x73,0xd2,0x10,0x70,0x8f,0x1e,0x84,0x70,0x47,0xb2,0x77,0x63,0x06,0x03,0x90,0x8f},18955,3998,67,15641,16652,0,21138,0}, + {{0x77,0x0e,0xff,0x89,0xa4,0xb2,0xde,0x3a,0x7f,0x62,0x01,0x32,0x6d,0x3c,0x38,0x46,0x13,0xe7,0xd1,0x50},22070,16961,46,9986,6805,22063,18459,0}, + {{0x77,0x21,0xa3,0xc2,0x34,0x7f,0x84,0x7b,0xf5,0x3a,0x61,0xb0,0x7b,0xb5,0xb0,0xaa,0x3d,0x03,0x9a,0xb0},19133,4441,46,9986,6805,19126,18459,4}, + {{0x77,0x6c,0x17,0x08,0xb3,0x59,0x21,0x4a,0x12,0x97,0x46,0x94,0xc0,0x27,0x34,0x3b,0x1f,0xef,0xd5,0x24},21321,13788,87,267,6805,0,21138,0}, + {{0x78,0xbd,0x59,0x64,0x24,0x1f,0x33,0xdb,0x13,0x26,0x71,0x6b,0xb3,0x46,0x58,0x63,0x23,0x0a,0x63,0x5d},21981,16728,46,17099,6805,0,18459,0}, + {{0x7a,0xb7,0xa9,0x01,0x61,0x62,0x86,0xad,0x5e,0x67,0x00,0xe3,0xb0,0x42,0x6d,0x25,0x60,0x92,0x60,0x6a},21153,14970,67,9652,6805,0,21138,0}, + {{0x7a,0xe9,0x4f,0x6d,0xc4,0x8a,0x3e,0x63,0x68,0x3e,0xb7,0x65,0xf8,0xc2,0x38,0x19,0xab,0x9f,0x30,0x1b},19253,5373,82,9663,6805,0,21138,0}, + {{0x7a,0xf0,0xf2,0xd1,0x3b,0x0e,0xc9,0x4e,0x81,0x8c,0x2e,0xcf,0x1a,0xdc,0x95,0xc8,0xcb,0xa5,0x05,0xe6},21278,13410,102,267,6805,0,21138,0}, + {{0x7b,0x3d,0x81,0x3b,0x62,0x23,0x93,0x46,0xdd,0x35,0x18,0x2c,0x88,0x13,0x69,0x62,0xc4,0x5f,0x02,0xa2},19422,5823,67,16480,6805,0,21138,0}, + {{0x7b,0xab,0x37,0x7c,0x7b,0xc3,0xa2,0x02,0xe9,0x66,0x75,0x0b,0x21,0x8a,0x12,0x53,0xd2,0x5d,0x89,0x23},20791,16220,46,6304,6805,0,18459,1}, + {{0x7d,0x73,0xee,0xa0,0x1e,0x37,0x20,0xe8,0xdf,0x5b,0xef,0x20,0xd4,0x39,0x85,0x6d,0x22,0x0f,0x0a,0x0e},18273,971,82,9663,6805,0,21138,0}, + {{0x7e,0x94,0xd3,0xcc,0xcf,0xbd,0x0f,0x43,0x93,0xd3,0xf2,0x1b,0x48,0x6c,0xa3,0xfb,0x86,0x50,0xac,0xbb},19141,4760,67,6304,6805,0,21138,0}, + {{0x7f,0x27,0xe9,0x85,0xac,0xf4,0xea,0x34,0xa2,0xf3,0x62,0x06,0xa8,0x96,0xd6,0x00,0x7d,0x7d,0x9d,0x83},18612,2701,51,9644,6805,0,18459,0}, + {{0x7f,0x6a,0x4f,0xf5,0x02,0x2e,0x81,0x14,0x86,0x53,0x41,0x50,0xb3,0xd5,0xa1,0x02,0xa3,0x68,0xed,0xc7},19914,7405,77,9663,6805,0,21138,0}, + {{0x80,0x4b,0x8e,0x05,0xba,0x22,0x20,0xe0,0x1a,0x1d,0x28,0xe0,0xea,0xc4,0xf3,0xdd,0x00,0xc4,0x36,0xb9},21510,14376,77,9652,6805,0,21138,2}, + {{0x80,0x84,0x6f,0xf8,0x2b,0x58,0x4c,0x92,0x3e,0x49,0xc0,0xd4,0x1a,0x2a,0x78,0x87,0xab,0x41,0xc6,0x60},20551,10073,67,7452,6805,0,21138,1}, + {{0x80,0xf5,0x3b,0xa3,0x44,0xb1,0xa6,0x81,0x74,0x8f,0x8e,0x3f,0xac,0x66,0xc8,0xc3,0x0a,0x88,0xba,0xeb},18107,16017,51,6140,16652,0,18459,1}, + {{0x81,0x61,0x11,0x7d,0xb5,0x3a,0xe1,0x49,0x93,0x69,0x18,0xb6,0x32,0x80,0x65,0x21,0x95,0x07,0x0b,0xa2},19675,6633,62,752,6805,0,18459,0}, + {{0x82,0x03,0x45,0x16,0xba,0x04,0x1a,0x36,0x34,0xb0,0xed,0xbe,0xd6,0x42,0x51,0x83,0x08,0xc1,0x03,0x09},19316,16507,62,11942,6805,0,21138,0}, + {{0x83,0x80,0x54,0x2f,0x6a,0x49,0x08,0xa4,0x4a,0xe5,0x27,0xc5,0x89,0xae,0xeb,0x05,0x2b,0xc5,0x93,0xbf},20517,9933,62,9701,6805,0,21138,0}, + {{0x84,0xde,0xac,0xbf,0x62,0x83,0xb2,0xbd,0x96,0xb1,0x51,0xcd,0x34,0x15,0x50,0xa2,0x88,0x1c,0xec,0xbe},19225,5403,62,15594,6805,0,21138,0}, + {{0x85,0x90,0x60,0xb5,0xad,0xa8,0x50,0x83,0xf5,0xe2,0x62,0x30,0x9f,0x5f,0x1b,0x7e,0xc7,0xe3,0x99,0x8c},21793,15526,72,9652,6805,0,20657,0}, + {{0x85,0xe7,0x06,0xd4,0xba,0xf6,0x07,0x86,0x05,0xf5,0xc3,0xd6,0xa9,0xe1,0x09,0x8c,0x34,0x61,0x1c,0x41},18130,277,67,691,6805,0,21138,0}, + {{0x86,0x43,0x5b,0xb5,0x32,0x9d,0xdd,0xa9,0x1e,0x76,0x29,0xaa,0xe8,0x29,0xf6,0x5e,0xd8,0xcd,0x41,0x8c},21947,16590,62,9986,6805,0,21138,0}, + {{0x86,0x6e,0x90,0xf0,0x8c,0xee,0x3c,0x58,0x20,0xb1,0x48,0xf6,0xc2,0xc0,0x79,0x1f,0x59,0x72,0xff,0x51},21563,13553,72,4679,6805,0,20657,0}, + {{0x86,0xd8,0xa3,0x56,0x53,0x8f,0x44,0xf8,0x91,0xae,0x98,0x5a,0x6f,0x52,0x74,0x02,0x50,0x33,0xbe,0xf5},21899,15349,67,15340,16652,0,18459,0}, + {{0x87,0x1f,0x5d,0x9c,0xe0,0x5f,0x25,0x02,0x4a,0x16,0x70,0x0a,0xb4,0x88,0x1b,0x65,0x75,0x87,0x4e,0xa4},18397,1526,62,11942,6805,0,21138,0}, + {{0x87,0x63,0x0a,0x36,0xc3,0x5b,0x46,0x6f,0xfe,0x42,0xe2,0x32,0xa3,0x08,0xc9,0x61,0x66,0x2f,0x15,0x80},19827,12379,46,17120,6805,0,18459,0}, + {{0x87,0xec,0x13,0x2c,0x7c,0x23,0x36,0x3f,0x7c,0x41,0x1f,0x9a,0xb0,0x97,0xd6,0x60,0xbc,0x7d,0xb5,0xf7},20159,8319,46,17781,16652,0,18459,0}, + {{0x88,0x51,0x44,0xde,0x10,0x7d,0x9b,0x65,0xc1,0x0e,0x37,0x37,0x32,0x26,0x34,0xa5,0xa3,0x2c,0x2f,0x9c},22253,17803,41,5914,6805,22248,18459,0}, + {{0x88,0xd8,0xb6,0xa0,0x84,0xb4,0xde,0x97,0x2b,0x4c,0x51,0x02,0x63,0x72,0x33,0x28,0xca,0x93,0xb1,0xbf},20499,9850,67,9663,6805,0,21138,0}, + {{0x8a,0x28,0xaf,0x48,0xbb,0xda,0x28,0xfa,0x78,0x2c,0x22,0x75,0x3e,0xb5,0x04,0x40,0x6f,0xe6,0x53,0x14},21021,11892,67,17167,16652,0,21138,1}, + {{0x8b,0x3f,0x9a,0x23,0xd2,0x07,0x71,0x84,0x4f,0x0c,0xf9,0xd1,0xc3,0x90,0x31,0x14,0x5f,0xf6,0x5d,0x2b},20412,9451,67,9663,6805,0,21138,0}, + {{0x8c,0x4d,0xea,0x9e,0xe6,0xb0,0x80,0xbd,0x37,0x51,0xdc,0xfa,0xda,0x64,0x35,0x5b,0x5d,0x48,0x0c,0x75},18970,3929,51,9986,6805,18949,18459,0}, + {{0x8c,0xdb,0xf9,0x88,0x15,0x58,0x61,0x40,0x8e,0x3f,0xbb,0x89,0x45,0x30,0x0b,0xc2,0x0d,0xd4,0x45,0x19},21411,14025,62,7471,6805,0,21138,0}, + {{0x8d,0x25,0xbb,0xfb,0xc5,0x68,0x18,0x72,0xe9,0x96,0xe2,0xa6,0x6d,0xdd,0x36,0xf0,0xe0,0xb3,0x5d,0x16},21705,14838,67,5933,16652,0,21138,0}, + {{0x8d,0x3b,0x7d,0x1a,0x81,0x17,0xb0,0x3a,0x72,0xd3,0x27,0x03,0x14,0xe0,0xff,0x08,0x33,0xf6,0xdf,0xf0},18639,16091,62,9156,6805,0,21138,0}, + {{0x8d,0x81,0x7d,0xd8,0x5d,0x8e,0xd8,0x27,0xce,0x49,0xc1,0xa1,0x61,0xbe,0xfe,0xf7,0x86,0xcf,0xf9,0x62},20389,17337,41,5914,6805,20383,18459,0}, + {{0x8d,0xc4,0xf4,0xc9,0x16,0x03,0x22,0x05,0x30,0xd3,0xf2,0x83,0xe7,0x88,0x64,0x66,0x94,0x88,0x96,0x82},20117,2087,62,9644,6805,0,21138,0}, + {{0x8d,0xc9,0xdd,0x1f,0x5d,0x2c,0x63,0x98,0x4d,0x44,0x52,0x9b,0xc6,0xb5,0xa8,0xa6,0x04,0x4d,0x82,0x35},20321,9201,67,5914,6805,20312,21138,0}, + {{0x8d,0xd0,0xae,0x2c,0xaa,0x84,0xac,0x70,0x22,0x14,0x54,0x5d,0x6f,0xbe,0x19,0x1c,0x66,0xb3,0xd7,0x7f},21056,11586,51,6322,6805,0,21138,0}, + {{0x8d,0xe8,0xa6,0x7b,0x7e,0x4a,0x7e,0x6e,0xb5,0xa0,0xca,0xe5,0xf0,0xfd,0xa8,0xe3,0xc6,0xd5,0x41,0x4e},182,157,97,5933,16652,0,21138,0}, + {{0x8d,0xe9,0xdc,0xb8,0x85,0x02,0xf2,0x98,0xa6,0x71,0x12,0x86,0xf0,0x6c,0x25,0x33,0xbd,0x16,0x3b,0x27},21458,14163,62,17099,6805,0,21138,0}, + {{0x8e,0x5b,0x83,0xda,0x25,0x84,0xab,0x8e,0x0a,0x4e,0x00,0x47,0x58,0xca,0x7b,0x79,0x36,0xe7,0x05,0x6d},20294,4302,62,5957,6805,0,21138,0}, + {{0x8f,0xd0,0x3b,0x0b,0x8c,0xc7,0x96,0x2d,0x6b,0xb8,0x89,0xb7,0x58,0x68,0xab,0xb7,0xaa,0x1f,0xf7,0x0d},22110,17040,46,5957,6805,22093,18459,0}, + {{0x90,0x79,0x5a,0xc8,0x1c,0xf0,0xd8,0x55,0x47,0xa8,0x4b,0x7c,0x01,0xc6,0x89,0xe0,0x35,0x00,0x90,0xa0},20603,1629,77,4897,6805,0,20670,2}, + {{0x91,0x39,0x9f,0xe8,0x9e,0xd5,0xec,0x2d,0xa6,0xc0,0x41,0x0e,0xe9,0x7d,0x28,0x09,0x74,0x12,0x76,0xd1},19394,5710,142,16305,16652,19386,21138,0}, + {{0x91,0x71,0x3a,0xf6,0x68,0xed,0x7e,0x5d,0x91,0x27,0x88,0x14,0x0d,0x8e,0x38,0x08,0xcf,0x9a,0x04,0x9f},18488,5784,51,10706,6805,0,18459,0}, + {{0x92,0x2a,0xc0,0x88,0x24,0xa1,0xbf,0xf6,0x9f,0x47,0x6a,0x66,0xc4,0x33,0xd1,0xda,0x38,0xd1,0x32,0xc3},20977,11790,62,2796,6805,0,21138,0}, + {{0x92,0x6e,0x94,0x11,0x98,0xdb,0x68,0xee,0x86,0x49,0x05,0xd1,0xa3,0x67,0xa8,0x16,0x93,0x67,0x3d,0x4a},18450,1676,67,5914,6805,0,21138,0}, + {{0x93,0x8a,0xe6,0xf8,0x5f,0xa3,0xf8,0x81,0xea,0xbd,0xc1,0xe0,0x1b,0x5f,0x3f,0x10,0x93,0x45,0xa7,0xfe},20490,9712,51,9701,6805,0,21138,0}, + {{0x93,0xa7,0xc1,0xa4,0xc8,0x5e,0x86,0xd6,0x6a,0xe9,0xc5,0x52,0xd5,0xf7,0x06,0xb2,0xe1,0x22,0xf3,0xbc},21607,14505,72,11759,6805,0,21138,0}, + {{0x96,0x07,0xcc,0xf6,0x3d,0xe1,0x20,0x2d,0x1c,0x79,0xa8,0x6a,0xf6,0xd6,0x08,0xb5,0x89,0x37,0x35,0xc5},21171,12689,77,5914,6805,0,21138,0}, + {{0x96,0x6a,0xc0,0x79,0x94,0x93,0xda,0x8e,0x48,0x71,0xff,0xa3,0x46,0x1b,0x9e,0x71,0xef,0x24,0xca,0xc2},19523,6099,72,5914,6805,0,21138,0}, + {{0x97,0x87,0x20,0xb4,0x88,0x73,0x68,0x28,0xbf,0xf3,0xd7,0xec,0xf2,0xf7,0x38,0xae,0xc4,0xaa,0x3f,0x36},19174,4957,72,5914,6805,19167,20657,0}, + {{0x97,0xc6,0x76,0xac,0x0b,0xd0,0xdd,0x60,0xb0,0x5a,0xa0,0x20,0xc4,0x8c,0x2f,0xb1,0xe2,0xb1,0x8c,0x05},18621,2729,51,9652,6805,0,18459,0}, + {{0x99,0x33,0xa4,0x67,0x0f,0x20,0x77,0x84,0x5e,0xc1,0xa0,0xea,0x81,0xa0,0xc0,0x0c,0x68,0xe4,0x07,0x2e},18143,509,72,11880,6805,0,21138,0}, + {{0x99,0xd0,0x80,0x12,0x70,0x24,0xb2,0xdb,0xc0,0x85,0x16,0xa2,0x2c,0xfe,0xf3,0xef,0xdc,0x58,0x61,0x2d},19012,10177,82,16480,6805,0,21138,0}, + {{0x99,0xe2,0xc7,0x3a,0xb7,0xdc,0x69,0x30,0xd3,0x37,0x9e,0xe4,0x3f,0xa3,0x34,0x2b,0x26,0xe9,0xb4,0xad},20960,11719,51,9644,6805,0,18459,0}, + {{0x9a,0x53,0xa3,0xd1,0x45,0x8f,0x15,0x32,0xea,0x50,0x8e,0x43,0x31,0x8e,0x9c,0x2b,0x4a,0xa1,0x10,0xad},22193,7240,72,9663,6805,0,21138,2}, + {{0x9b,0x11,0xe9,0x3e,0x42,0xea,0x5b,0x72,0x76,0x76,0x38,0xbf,0x66,0x82,0xd5,0x59,0x85,0x0f,0xba,0x72},22119,17146,82,16480,6805,0,21138,0}, + {{0x9b,0xb8,0x16,0x09,0x55,0xff,0xf2,0xa4,0x03,0x68,0xa8,0x2a,0x3b,0x38,0x79,0x08,0x83,0x79,0x64,0xb4},22078,17001,51,9986,6805,0,18459,0}, + {{0x9c,0x46,0xa0,0xcc,0xdc,0x9c,0xba,0xeb,0x8d,0x85,0x6c,0x80,0x1e,0x67,0xfd,0xbb,0x26,0x28,0x0a,0xf6},22240,17762,72,14072,6805,0,21138,0}, + {{0x9c,0x80,0xa4,0x8a,0x5c,0x3e,0xf7,0x19,0x02,0x89,0xf4,0x6b,0x50,0x23,0x55,0x0d,0xfe,0x8f,0xbb,0xd4},20722,10925,62,1448,16652,0,21138,0}, + {{0x9e,0xea,0x2a,0x1d,0x02,0x36,0xa1,0x2c,0xfa,0x35,0x6e,0x6c,0xde,0x3e,0x64,0xe3,0xcf,0x33,0x4f,0x1a},19325,16548,67,11942,6805,0,21138,0}, + {{0x9f,0xab,0x62,0xa6,0xd3,0x13,0x85,0x1a,0x8b,0xa1,0xdc,0xc2,0x4f,0xf4,0xd5,0x34,0x66,0x36,0x44,0x64},19199,637,67,7452,6805,0,21138,1}, + {{0x9f,0xd0,0x2d,0xbf,0x78,0xa1,0xff,0xa4,0xaf,0x67,0xac,0x2e,0xf2,0xea,0x3c,0xd2,0x9e,0x41,0x37,0x3c},20237,8993,72,9663,6805,0,21138,0}, + {{0xa0,0x24,0x59,0xfc,0xa4,0x18,0x80,0x5d,0x96,0x58,0x44,0x7c,0xd8,0x67,0x57,0xf3,0xeb,0xa2,0x21,0x58},19066,4560,62,1899,16652,0,21138,0}, + {{0xa0,0x27,0xc4,0x1b,0x2e,0x3e,0x35,0xcd,0xae,0xe7,0xfd,0x3c,0x7e,0xa2,0xd0,0x9b,0x5e,0xcf,0x7f,0x9f},19002,4183,67,11942,6805,18993,21138,0}, + {{0xa0,0x48,0x3f,0xee,0x2b,0xf6,0x5b,0x49,0x60,0x0d,0x15,0xb3,0x94,0x5f,0xb6,0x65,0x58,0x9b,0xbf,0x3e},18188,5089,67,15520,6805,0,18459,0}, + {{0xa0,0x80,0x42,0x9a,0xf8,0x21,0xcd,0x37,0xff,0xb7,0xa5,0xc0,0x16,0xdc,0x63,0xdf,0xc5,0xa5,0x11,0x91},19208,5072,62,1393,6805,0,21138,0}, + {{0xa0,0xf7,0x3f,0x14,0x6b,0xfa,0x64,0x55,0xef,0x91,0x6c,0xbe,0x6c,0x76,0xb9,0x3b,0x7d,0xf7,0xf9,0x99},21784,15503,51,6304,6805,0,18459,0}, + {{0xa1,0x16,0x03,0xae,0xc3,0x13,0xeb,0xb1,0x69,0xe0,0x3b,0x59,0x4c,0xa4,0x1a,0x0d,0xed,0xcd,0xbf,0x40},21927,16394,72,7471,6805,21918,21138,0}, + {{0xa1,0x79,0x77,0xb3,0x3a,0x87,0xe3,0x7e,0xb1,0xe8,0x58,0xd7,0x78,0x54,0x97,0xfc,0x4a,0xd3,0x4d,0x66},21908,15987,62,11880,6805,21899,18459,0}, + {{0xa1,0x9c,0x5f,0xa5,0x05,0x09,0x8c,0xe6,0xa1,0x1b,0x77,0x8f,0x10,0xb9,0x0d,0x58,0x21,0xf6,0xf0,0xae},18746,3406,72,9652,6805,0,21138,0}, + {{0xa1,0xf6,0xc2,0x0a,0x72,0x94,0x7c,0x38,0x0c,0xbd,0x52,0x19,0x9e,0x4e,0x50,0x0d,0x51,0x9f,0x12,0xa8},20648,7326,67,11927,6805,0,21138,0}, + {{0xa2,0x16,0x66,0xff,0x45,0xb6,0x73,0x20,0x43,0xda,0x46,0x8d,0xe7,0xac,0xbe,0x1d,0xbc,0xa1,0xd5,0xab},18232,826,62,9156,6805,0,21138,0}, + {{0xa2,0xa2,0xe4,0x10,0x8e,0xed,0x45,0x3e,0x95,0xf0,0x80,0xb7,0x46,0xb0,0x79,0x71,0x17,0xb8,0x86,0xe9},19038,17494,77,5957,6805,0,20657,0}, + {{0xa2,0xbd,0xfd,0x8e,0xd6,0x05,0xa5,0x73,0x02,0xee,0xc5,0x23,0xca,0x9b,0x23,0xc2,0x38,0x81,0xbc,0x66},20994,11812,41,9986,6805,20099,18459,0}, + {{0xa2,0xc6,0x9b,0xbe,0x0c,0xab,0x5c,0xa9,0xc6,0xf8,0x8f,0x61,0xef,0x46,0xe4,0x7d,0x6a,0x54,0x65,0x72},22217,16656,62,5957,6805,0,21138,0}, + {{0xa3,0xb7,0xb4,0x55,0x50,0xee,0xf3,0x7f,0x2f,0x85,0xfe,0x2d,0x98,0x33,0x43,0x41,0xcc,0xd6,0x78,0x40},18182,16041,72,691,6805,0,20657,0}, + {{0xa3,0xfe,0xaf,0x6a,0x44,0xce,0xa5,0x5e,0x8c,0x34,0x58,0xae,0x19,0xeb,0x3a,0x6d,0xeb,0x12,0xb0,0xb8},19471,5977,62,5957,6805,0,21138,0}, + {{0xa4,0x1e,0x89,0xc3,0x03,0x72,0x19,0x04,0x29,0x8a,0x36,0x9b,0x6a,0xea,0x5c,0x42,0xbc,0x5a,0xea,0xc3},21303,13651,62,9701,6805,0,18459,0}, + {{0xa4,0x4c,0x2e,0x48,0x09,0x24,0x20,0x2e,0xae,0x9d,0xf3,0xb1,0x9a,0x01,0x62,0x0d,0x7b,0xc0,0xef,0x2c},19343,5597,72,16480,6805,0,21138,0}, + {{0xa5,0x06,0x8f,0xeb,0x9b,0x69,0x7d,0x7f,0x6a,0xe7,0x88,0x51,0x4a,0xf0,0xa1,0x81,0x2f,0x29,0x3f,0x7b},20383,9390,41,17167,16652,0,18459,0}, + {{0xa5,0x1e,0x34,0x86,0x93,0x40,0x0a,0x51,0x44,0x3b,0x45,0xe2,0x63,0x2a,0xcc,0xae,0xb5,0x61,0xab,0xd8},20621,1300,72,5914,6805,0,21138,0}, + {{0xa5,0xed,0x92,0x5b,0xc8,0x80,0xab,0x8a,0x95,0x46,0x97,0x3b,0x88,0xe8,0xcb,0x32,0xbf,0xac,0x50,0xde},19749,6441,72,9663,6805,0,20657,0}, + {{0xa6,0x40,0xc7,0x24,0x1a,0x0c,0x04,0x1e,0xb2,0x5f,0x73,0x65,0x65,0x45,0xb3,0x2d,0xa3,0x10,0xd0,0x20},21209,12934,72,9947,6805,0,20657,0}, + {{0xa6,0x7b,0xe4,0x3f,0x8e,0x0d,0x45,0x3b,0x17,0x8c,0x08,0x7a,0x7d,0xcf,0x77,0xd2,0x09,0xdc,0xef,0xef},18292,13062,62,17099,6805,18286,21138,0}, + {{0xa8,0x0b,0x9c,0x91,0x78,0xb6,0x0e,0xfd,0x84,0xa9,0xec,0x5d,0xe4,0xdd,0xdc,0x59,0xe8,0xf6,0x21,0xe3},20765,11033,77,9663,6805,0,20657,0}, + {{0xa8,0x84,0x6a,0x72,0xf5,0xeb,0x81,0x9c,0x6e,0x9b,0x69,0x47,0xea,0xa2,0x15,0x97,0x44,0x6f,0x2e,0x85},19126,4463,46,15641,16652,0,18459,6}, + {{0xa8,0x91,0x13,0x42,0x63,0xf2,0x7d,0xe7,0x7d,0x2b,0xa2,0xdd,0x51,0x2c,0x52,0x95,0xe7,0x14,0x8b,0x69},19661,6327,62,2138,16652,0,18459,2}, + {{0xa8,0xdc,0x13,0x7d,0xd0,0xdd,0x80,0x25,0x9d,0xca,0xac,0xc1,0x32,0xcc,0x56,0xe6,0xd7,0x31,0xd4,0x4e},21572,15023,62,5914,6805,0,21138,0}, + {{0xa9,0x2d,0x73,0x6e,0x83,0x2b,0x4e,0x88,0x01,0x09,0xa4,0x4b,0x9c,0xc0,0x9e,0x81,0x85,0x1c,0x5c,0x14},18204,796,46,9986,6805,0,18459,0}, + {{0xa9,0x8a,0x92,0x07,0x42,0xf2,0xf2,0x39,0x9c,0x58,0x8c,0x70,0x2a,0x14,0xfd,0x83,0xc7,0xf0,0x3d,0x46},21366,8636,72,9986,6805,21357,21138,0}, + {{0xa9,0xdf,0x7e,0xa9,0x3b,0x26,0x8b,0x72,0x26,0xca,0x9f,0x38,0x51,0x04,0x9c,0x82,0xaa,0x11,0x5f,0x90},21696,14923,46,9144,6805,0,18459,0}, + {{0xaa,0x37,0x2a,0x72,0x9b,0xb9,0x1d,0x73,0x08,0x6e,0x84,0x5e,0xfb,0x30,0x7d,0xa0,0x61,0x21,0xdc,0x72},22182,17575,62,9986,6805,0,21138,0}, + {{0xaa,0xae,0x3b,0xd2,0x25,0xa4,0x3d,0xc6,0xf7,0x30,0x01,0xa6,0x56,0xe4,0x6e,0x9c,0x5a,0xf2,0xe6,0xc3},19307,7478,77,5914,6805,0,21138,2}, + {{0xab,0xfc,0x4f,0x06,0xd4,0x92,0xf5,0x3c,0xf7,0x3e,0xeb,0x5c,0x36,0xdb,0x23,0x61,0xf6,0xe8,0x89,0xa5},21330,13694,77,267,6805,0,21138,0}, + {{0xac,0x03,0x3b,0x09,0xb9,0x58,0x55,0x63,0xd7,0x30,0x0e,0xa6,0xb7,0xcc,0x53,0x49,0x4f,0x06,0x33,0xb5},20108,2040,62,9644,6805,0,18459,0}, + {{0xad,0x0e,0x36,0xfc,0xac,0xec,0xeb,0x0d,0x15,0x3f,0xf2,0x72,0x83,0x96,0x1d,0x75,0x7a,0x53,0x34,0x33},18993,4166,67,11942,6805,0,21138,0}, + {{0xad,0x51,0xf8,0x48,0xd8,0xd7,0xb0,0xf3,0xad,0x62,0xa7,0x4d,0xb5,0x4d,0xf4,0x08,0x3e,0x25,0xa1,0xf8},18630,2754,62,9144,6805,0,21138,0}, + {{0xaf,0xad,0xe5,0xdd,0x16,0x1d,0x71,0xae,0x64,0x6f,0x3d,0x23,0x61,0xae,0xf5,0x61,0x00,0x36,0x21,0x60},19758,6560,77,9663,6805,0,20657,0}, + {{0xaf,0xcc,0x82,0x69,0xfd,0x2a,0xba,0x58,0x08,0x44,0x53,0x24,0x9f,0x74,0x7f,0xd3,0x68,0x30,0x7c,0xe6},21625,14546,62,5914,6805,0,21138,0}, + {{0xb0,0x35,0x87,0x42,0xfc,0x78,0x93,0xb1,0xf0,0x94,0x9c,0x81,0xda,0x91,0x9d,0x10,0x9b,0x5d,0xef,0xe8},18923,4210,46,9986,6805,0,18459,0}, + {{0xb0,0xaf,0x74,0xf3,0x66,0xca,0x52,0x84,0xfb,0x94,0x47,0xdf,0x1a,0x45,0x6e,0x7f,0x62,0x42,0x50,0xf0},20133,8271,82,9652,6805,0,20657,0}, + {{0xb1,0x60,0xd9,0xbb,0x9d,0x93,0x89,0xfa,0x78,0x50,0xb0,0x8e,0x23,0x6b,0xe4,0xd1,0xec,0xda,0xf8,0x85},19532,10364,67,13847,6805,0,21138,0}, + {{0xb1,0xd9,0xea,0x7d,0x1e,0x3e,0xc6,0x56,0x6b,0xf7,0xc8,0x99,0xf6,0xea,0xfc,0x87,0xfa,0xce,0x7f,0xd4},18158,579,77,9652,6805,0,21138,0}, + {{0xb1,0xe4,0x2a,0x5f,0x72,0xe4,0x64,0x28,0xb8,0x1e,0x6d,0x1d,0x43,0xab,0x96,0x6e,0x6d,0x51,0x11,0x0a},22146,17247,67,9701,6805,0,21138,0}, + {{0xb1,0xe5,0x9c,0x9e,0xa6,0xa5,0x3e,0xb6,0x40,0x35,0xef,0x5c,0x56,0x20,0x17,0xe9,0x6c,0x95,0xb4,0x6f},18930,3845,72,9663,6805,0,21138,0}, + {{0xb2,0xa9,0x75,0xb5,0xd6,0x09,0x00,0x9b,0xec,0x8a,0x89,0xc5,0xc9,0x1e,0x42,0xe2,0x04,0x9c,0x98,0x2f},19612,6178,67,9947,6805,0,21138,1}, + {{0xb2,0xc3,0x6f,0x37,0x7d,0x0f,0xef,0xa7,0x98,0xe3,0xf5,0xda,0x59,0x95,0xc7,0x69,0xda,0x84,0x41,0xfa},18790,3119,62,5914,6805,0,21138,0}, + {{0xb3,0x1e,0xf6,0x82,0x53,0x4f,0x8b,0xfe,0xe6,0x4f,0x6b,0x90,0x72,0x34,0xb2,0xad,0x3b,0x6e,0x09,0xe5},19854,12176,41,9652,6805,0,18459,0}, + {{0xb3,0x5d,0x11,0xe0,0xde,0x73,0xfd,0x19,0x13,0x88,0x64,0xbe,0xb7,0x04,0x14,0xbc,0x5f,0x3b,0xba,0xc3},19101,4264,97,6310,16652,21625,21138,0}, + {{0xb3,0x8d,0x63,0x31,0xfd,0x00,0x74,0x8d,0x65,0xae,0x1d,0x1f,0xac,0x3d,0xdf,0x31,0xc2,0x90,0xeb,0x9e},21881,15944,67,9986,6805,0,21138,0}, + {{0xb3,0xb8,0xbc,0xd6,0x6f,0x58,0xee,0x57,0xf0,0xe7,0x47,0x3d,0xf1,0x54,0x9f,0x51,0xe9,0x09,0x6a,0xde},22132,17405,72,5914,6811,22128,20657,0}, + {{0xb3,0xd3,0xeb,0xea,0x20,0xe3,0x7e,0xac,0x9c,0x76,0xa0,0xb8,0xaa,0x97,0xd7,0x69,0xf1,0x18,0x8c,0xe4},19445,13367,67,17099,6805,19437,18459,2}, + {{0xb4,0x5f,0xa3,0x54,0x03,0xc7,0xd5,0x36,0xa1,0x10,0xca,0xa5,0x36,0xa0,0x59,0xdf,0xdb,0x0f,0x8a,0xf3},21431,14078,46,9644,6805,0,18459,0}, + {{0xb4,0x81,0x37,0xf4,0xeb,0x68,0xac,0xfd,0x4e,0x00,0x04,0xf7,0xaa,0xbb,0xa4,0xdd,0x17,0xc6,0xa6,0x9e},18781,3200,77,9652,6805,0,21138,0}, + {{0xb4,0x9f,0xf2,0x23,0xc9,0x30,0x65,0x3b,0xb8,0xf8,0xc1,0xbe,0xec,0xae,0xdd,0xfd,0x36,0xe4,0x54,0xa2},19906,16144,62,9156,6805,0,21138,0}, + {{0xb5,0xc9,0x04,0x8c,0x60,0xdf,0x6b,0x53,0xae,0x45,0x1a,0xbe,0x18,0x5b,0xc2,0x99,0xb3,0x90,0x66,0x6c},18688,3104,82,9684,6805,0,21138,0}, + {{0xb5,0xd2,0xde,0xdf,0x90,0xa5,0xb9,0x0c,0x4d,0x2b,0x58,0x8e,0x5f,0x69,0x85,0xb8,0xf3,0x8e,0x18,0xb5},19167,4911,72,5914,6805,0,20657,0}, + {{0xb6,0xdd,0x43,0x5d,0x43,0x78,0x89,0xe9,0xf4,0xdf,0x6f,0x06,0x94,0x14,0x9d,0x52,0x33,0xc7,0xa6,0x5e},20058,8138,62,2138,16652,0,18459,0}, + {{0xb7,0x43,0x17,0x48,0x1e,0xf2,0x01,0x52,0x74,0xbc,0x09,0x1e,0x2c,0xf2,0x3f,0xed,0x08,0xa7,0x12,0x1e},19262,5496,46,9986,6805,0,18459,0}, + {{0xb7,0x47,0x5b,0x26,0x17,0x32,0xfe,0x57,0xfa,0x4f,0xa2,0xb3,0x9f,0xc5,0x37,0x20,0xe2,0xd8,0x2a,0x48},20004,7519,51,9644,6805,0,18459,0}, + {{0xb7,0x8f,0x8b,0xd1,0x06,0x30,0x2a,0xa1,0x70,0x5e,0x7c,0x14,0xd6,0x92,0x52,0xb5,0x20,0x1b,0xd3,0x06},20918,11475,51,752,6811,0,18459,0}, + {{0xb7,0xde,0x22,0xa4,0x20,0xdf,0x3b,0x94,0x3a,0xa5,0xd3,0x29,0x78,0x66,0x83,0xd0,0xb9,0xa6,0xdd,0x66},21227,12991,77,9947,6805,0,21138,0}, + {{0xb8,0x4b,0x97,0x76,0x3c,0xb1,0x73,0x42,0x67,0xef,0xb3,0x1f,0x37,0x01,0xc8,0x53,0xd0,0xe8,0x52,0xbf},19430,13222,51,13962,16652,0,18459,2}, + {{0xb8,0xf6,0x96,0xe6,0x0f,0x5a,0xdb,0x3e,0xd9,0x68,0x88,0x45,0x74,0xf2,0x18,0xf4,0x16,0xb3,0x44,0xee},22063,16945,62,15641,16652,0,18459,0}, + {{0xb9,0xfe,0x47,0x9e,0x56,0xf5,0xde,0x00,0x71,0x9d,0xd1,0xd7,0x45,0xea,0x9a,0x93,0xaf,0xe5,0x90,0x27},19496,10222,62,13847,6805,0,21138,0}, + {{0xba,0xa6,0xd0,0x2d,0x43,0x5d,0xec,0x6d,0x21,0x19,0x62,0x8a,0x18,0x20,0xf3,0x51,0xa2,0x46,0x42,0x58},21536,14419,51,9652,6805,21528,18459,0}, + {{0xbb,0x0a,0x3c,0x91,0x0f,0xfe,0x4a,0xb6,0xa3,0x75,0x40,0x19,0x4b,0xa3,0x55,0x7a,0x85,0x6d,0x86,0x91},18122,445,77,9644,6805,0,21138,0}, + {{0xbb,0x3b,0xe2,0x10,0x32,0x45,0xd0,0x45,0x2c,0xdf,0x93,0xee,0x4f,0x44,0x35,0x7e,0x74,0xda,0xf7,0x65},18579,2590,67,14683,16652,0,21138,0}, + {{0xbb,0x80,0x36,0x34,0x87,0xd0,0x87,0xbe,0x66,0xf4,0x33,0x33,0xcc,0x9c,0x7d,0xe5,0xdc,0x06,0x54,0x15},18286,13036,62,11246,16652,0,21138,0}, + {{0xbb,0x82,0x81,0x10,0x3f,0x60,0x8f,0x79,0x8d,0x03,0x46,0xde,0xc7,0x46,0xf1,0x16,0x5b,0x83,0x5b,0xa3},18331,1232,72,5914,6805,0,21138,0}, + {{0xbb,0xb6,0x61,0xe3,0x02,0x5f,0x67,0x02,0x47,0x0f,0x55,0xb9,0xcb,0x03,0x31,0x2d,0x19,0x7a,0x23,0x07},21244,13127,62,292,6805,0,18459,1}, + {{0xbc,0x81,0x6c,0xf3,0xa6,0xba,0x32,0xe8,0xaa,0xe2,0x0e,0x76,0x9f,0x84,0x75,0x83,0x2c,0x66,0x99,0x31},19577,10317,102,13847,6805,0,21138,0}, + {{0xbc,0xcd,0x9b,0xbe,0x17,0x3a,0xe1,0x7c,0x04,0x85,0xa2,0xa7,0x53,0x75,0xab,0x01,0x26,0xbe,0x82,0xfb},21819,15655,77,9652,6805,0,21138,0}, + {{0xbe,0x12,0x1f,0x1b,0x42,0x68,0x58,0x99,0x26,0xa8,0x25,0x1c,0x03,0x46,0xb6,0x4c,0x05,0xf3,0x85,0x92},21348,7072,62,9644,6805,0,18459,0}, + {{0xbf,0xe2,0x8b,0xfa,0x08,0x2b,0x20,0x75,0x91,0x57,0xa5,0x04,0x22,0x82,0xe3,0x21,0x6d,0xf6,0xb5,0x43},18570,2374,62,9986,6805,0,21138,0}, + {{0xbf,0xf0,0x1c,0x94,0x9a,0xe6,0x6b,0x51,0x79,0x7e,0xc8,0x91,0xbe,0xfd,0x91,0x00,0x95,0x91,0xfb,0xc1},18816,467,67,7471,6805,0,21138,1}, + {{0xc0,0x2b,0x28,0x28,0x8a,0x06,0x12,0xbf,0x52,0xd5,0xd5,0x79,0x3e,0x19,0x3d,0x92,0x6a,0x90,0x1b,0x56},18843,3432,51,5914,6805,18852,21138,0}, + {{0xc0,0x33,0xd9,0x28,0x35,0xf0,0x4f,0x3b,0xaf,0xb6,0x2b,0xb3,0x79,0x8c,0x5e,0xb6,0xfc,0x54,0x69,0x0b},20713,10862,51,5914,6805,0,21138,0}, + {{0xc0,0x76,0xf4,0x7e,0xa0,0x6f,0x68,0x9e,0x8f,0x77,0xd6,0x60,0x7c,0x21,0xf4,0xe5,0xb4,0xb4,0xf3,0x68},20508,9892,72,5914,6805,0,21138,0}, + {{0xc1,0xae,0x36,0x8c,0xa0,0x20,0x33,0xf5,0x65,0x57,0x1c,0xdc,0xac,0xb4,0x0c,0x1e,0x5f,0x51,0x05,0xb4},21402,14002,77,9947,6805,0,21138,0}, + {{0xc1,0xce,0x05,0xb1,0x6e,0xf1,0xc4,0x49,0xca,0xc7,0xe5,0x71,0x6b,0xa8,0x45,0xa1,0x1d,0x1e,0xe6,0xb9},20739,11008,72,11942,6805,0,21138,0}, + {{0xc1,0xe6,0x6f,0x43,0x22,0x72,0x5b,0xda,0x15,0xe1,0xd6,0x59,0x41,0xbe,0x63,0xf7,0x82,0x8a,0x24,0x64},20585,10662,72,2225,6805,0,21138,0}, + {{0xc1,0xf2,0x1b,0xc4,0x71,0xf2,0x27,0x92,0x10,0xc6,0x0d,0x1c,0x03,0xf0,0x15,0x91,0x06,0x13,0x1e,0x96},21180,12740,77,5914,6805,21171,21138,0}, + {{0xc2,0xc1,0xfd,0xb9,0x35,0x06,0x84,0x05,0x15,0x4e,0xca,0x2a,0xb6,0xa4,0xed,0x68,0x98,0x53,0x53,0xdd},20140,8179,72,9663,6805,0,20657,1}, + {{0xc3,0x39,0x2b,0x42,0x38,0xb7,0xd5,0xf4,0xa4,0x0e,0x4b,0x3d,0x24,0xd6,0xed,0x0d,0x5f,0xd0,0x38,0xdf},18670,2806,92,9663,6805,0,21138,0}, + {{0xc3,0xca,0x89,0xcf,0xa1,0xd1,0x1a,0x64,0x9c,0xe1,0xf2,0x56,0x5a,0x1a,0xa2,0xfb,0x3d,0xda,0x9a,0x07},20936,11507,62,5914,6805,0,18459,0}, + {{0xc4,0x3c,0xa0,0x0c,0xb4,0x6a,0x53,0x00,0xda,0x6d,0xbb,0x85,0x33,0xff,0x52,0xca,0xa9,0x2b,0xdc,0xbc},21835,15884,62,5914,6805,0,21138,0}, + {{0xc5,0x02,0x69,0xa7,0x70,0x64,0x83,0x55,0x2b,0x03,0xca,0xc0,0x7c,0x06,0xd1,0xdd,0x2b,0x21,0xa8,0x6b},18315,1055,36,5914,6805,0,18459,0}, + {{0xc5,0xbf,0x0e,0x98,0x15,0x9e,0x57,0x29,0x5a,0xad,0x65,0x5b,0x34,0x0a,0xcc,0xc5,0xd5,0xff,0xdf,0xc2},18308,1036,97,3901,16652,0,18459,0}, + {{0xc8,0x99,0x62,0x8c,0xad,0x20,0x7b,0x43,0x33,0xca,0x14,0x19,0xf0,0xaa,0x35,0x52,0x74,0xe4,0x83,0x1d},21999,16805,51,17099,6805,0,18459,0}, + {{0xc8,0x9f,0x4e,0xcb,0x16,0x6f,0xc3,0x46,0xd4,0x48,0x9f,0x05,0x6c,0x7d,0x9d,0x11,0xf8,0x56,0x81,0x3e},19941,7533,51,7652,6805,0,21138,0}, + {{0xc8,0xcf,0xad,0x52,0x93,0xdc,0xf2,0xd2,0x08,0x7c,0xa0,0x36,0x49,0x01,0x2a,0x20,0xd0,0xf6,0x89,0x7a},19352,16168,97,3901,16652,0,18459,0}, + {{0xcb,0x1c,0x39,0x87,0xec,0x85,0xf5,0x5a,0x21,0x53,0x3b,0x5e,0xad,0x58,0x16,0x92,0xbd,0x06,0xa3,0xa9},19541,10398,67,13847,6805,0,21138,0}, + {{0xcb,0xbf,0xde,0x36,0x85,0x7e,0xb3,0xa7,0x8f,0x91,0x8a,0x3e,0xa8,0xb8,0x10,0x9b,0xe3,0x58,0xd1,0x51},19898,7307,62,9986,6805,0,21138,0}, + {{0xcb,0xd8,0x79,0x23,0x48,0xd2,0x6d,0x5e,0xe0,0x4e,0xb0,0x71,0x87,0xcb,0x17,0xcc,0xb7,0xcb,0x5e,0xed},20464,9576,127,4665,16652,20455,21138,0}, + {{0xcc,0x1b,0xb9,0xc2,0x26,0x2a,0xbe,0xe9,0x0a,0xda,0xe8,0x06,0xb5,0x4f,0xc2,0xaa,0x85,0xde,0x4f,0x9a},21376,13926,67,8963,16652,0,21138,0}, + {{0xcc,0x53,0x65,0x7f,0xd7,0xf2,0xa8,0xfa,0x8e,0x7c,0xa0,0x17,0x96,0xeb,0x37,0x78,0x54,0xfd,0x50,0x14},21501,14293,152,9958,0,0,21138,0}, + {{0xcc,0x6a,0x2d,0x0a,0x48,0x26,0xeb,0x98,0xb5,0xfd,0xe9,0x99,0xed,0xa5,0x25,0xc4,0xb1,0x22,0xe3,0x49},21252,13163,62,6140,16652,0,21138,0}, + {{0xcc,0xd5,0x6b,0x03,0xb2,0x1d,0xc3,0xda,0xd1,0x56,0xe8,0x28,0x51,0xe7,0x35,0x7a,0x7b,0x66,0xdc,0x48},19150,4787,41,5914,6805,0,18459,0}, + {{0xcd,0x16,0xc4,0xd9,0xc4,0xea,0xf6,0x9f,0xf8,0xef,0x05,0x49,0x6a,0x98,0xb4,0xc8,0xeb,0xd5,0x58,0xfb},21528,14439,62,9652,16652,0,18459,0}, + {{0xcd,0x96,0x23,0xeb,0x74,0x24,0x35,0x21,0x34,0xec,0x75,0x14,0x53,0x05,0x4d,0x44,0xcb,0x49,0x9f,0x5b},19595,10543,72,13847,6805,0,21138,0}, + {{0xcd,0x99,0xce,0x7f,0x8b,0xf5,0x17,0x17,0x7e,0x0d,0xbf,0xd2,0x43,0x1a,0x72,0x67,0xcd,0x3d,0x59,0xdf},21759,15389,67,16494,6805,0,21138,0}, + {{0xcd,0xb1,0xec,0x8d,0x3a,0x03,0x2f,0xb4,0xef,0x3d,0x63,0xe2,0xf8,0x5a,0xf1,0x5d,0x21,0x79,0xb4,0xbb},21652,14638,82,15417,6805,0,21138,2}, + {{0xcd,0xde,0xcd,0x29,0x14,0xb4,0x21,0xdc,0xb4,0x56,0x9b,0x13,0xbd,0x12,0x74,0x36,0x1f,0x72,0x12,0x98},22310,18065,67,8969,6805,0,21138,0}, + {{0xce,0xdb,0x89,0x2d,0x84,0x12,0xa3,0x35,0x48,0x0a,0xf0,0x77,0x5e,0xb7,0x6b,0x67,0x9f,0x77,0xd0,0x63},19234,5445,67,16346,16652,0,21138,0}, + {{0xd0,0x92,0x51,0xbb,0xcc,0x78,0xe2,0xa1,0x96,0xcf,0xd1,0x23,0x4f,0xe7,0xa9,0xe8,0x4e,0x53,0xe3,0x94},18391,1480,51,11942,6805,0,18459,0}, + {{0xd0,0xb8,0x37,0xb0,0x4c,0xca,0x8a,0xb3,0xdd,0xe7,0x2e,0xea,0xe3,0xc2,0xcc,0x5e,0x10,0xdb,0x8e,0x73},20532,6522,46,17099,6811,20523,18459,0}, + {{0xd0,0xb8,0x57,0xba,0xa8,0x88,0xb0,0x1a,0xdd,0x75,0x91,0x59,0x6e,0x20,0x93,0xfa,0x8d,0x3d,0x59,0x8e},19056,4504,67,11880,6805,19047,21138,0}, + {{0xd0,0xbd,0xaf,0x93,0xcc,0xea,0x98,0x89,0x93,0x34,0xcd,0xfc,0xcc,0xae,0xb6,0xc5,0x9d,0x78,0x99,0x80},18764,3650,67,4110,16652,0,21138,0}, + {{0xd1,0x19,0x9d,0x24,0x2b,0x67,0xc4,0x01,0x02,0x01,0xb6,0xc3,0x64,0xf2,0x2f,0x55,0xec,0x67,0xbb,0xa0},22282,5314,77,5914,6805,0,21138,0}, + {{0xd1,0x36,0xfe,0x98,0x24,0x0d,0xba,0xf5,0xbb,0x30,0x16,0xd8,0x7d,0xd2,0x0e,0xb6,0x5e,0x13,0xe8,0xc5},22026,16684,122,10157,0,0,21138,0}, + {{0xd1,0x6b,0xab,0x64,0x45,0x5f,0x20,0x6f,0x9b,0x71,0xa2,0x74,0xc4,0xa2,0xf7,0xcb,0x43,0xc9,0x8a,0x18},21714,14859,41,9986,6805,0,18459,0}, + {{0xd1,0xc5,0xf3,0x65,0x92,0x56,0x5e,0x32,0x84,0xbd,0xc9,0x70,0xb6,0x73,0xfd,0x7b,0x57,0x7a,0xd7,0x84},21076,12477,72,5914,6805,21067,20657,0}, + {{0xd3,0x52,0xb7,0xaf,0xa1,0xb0,0xd7,0x63,0x30,0x50,0x4b,0x3e,0xbd,0x81,0x2e,0xa6,0xd7,0x6b,0x16,0x26},18404,1846,67,9652,6805,0,21138,0}, + {{0xd3,0x7a,0xdd,0xaf,0x2c,0xc3,0xa3,0x7c,0x84,0x30,0x84,0x25,0x70,0xe1,0x84,0x6e,0xa6,0xaf,0xe0,0x2a},20951,11654,62,9644,6805,0,18459,0}, + {{0xd3,0xa0,0x71,0xfc,0xe1,0xf5,0x0d,0x5b,0xaf,0x67,0xd3,0x21,0x0a,0x00,0xc5,0xbd,0x0e,0xe0,0x3c,0x1a},20246,9037,77,10706,6805,0,21138,0}, + {{0xd7,0x66,0x42,0x86,0xc3,0xf2,0x2d,0x5b,0xbd,0x01,0xb3,0x38,0xbc,0x4b,0x97,0x29,0x27,0xf6,0x34,0xf3},21440,14118,77,9663,6805,0,21138,0}, + {{0xd7,0x70,0x23,0x30,0x35,0x50,0x53,0xc3,0x16,0x69,0xd3,0xdc,0xff,0x65,0xb4,0x2e,0xae,0x4b,0x07,0xb7},21687,14778,67,9701,6805,0,21138,0}, + {{0xd7,0x9c,0xe7,0x68,0x84,0x9f,0x21,0xa9,0x01,0x0c,0x22,0x1d,0x8d,0xd7,0x81,0x7a,0x4a,0x1b,0x94,0xf9},22173,17443,77,9663,6805,0,20657,0}, + {{0xd7,0xcc,0x23,0x68,0x64,0x76,0xb7,0xe0,0x2a,0x32,0x2e,0xa3,0xbc,0xd7,0x61,0x82,0xf8,0x31,0xdd,0x1e},18977,4033,62,9947,6805,0,21138,0}, + {{0xd8,0x3a,0x6b,0x22,0x18,0xbf,0xb9,0x58,0x37,0xb9,0xfa,0x8e,0x43,0x96,0x3c,0xff,0xbc,0x20,0xdc,0x17},18257,842,72,14138,6805,0,21138,0}, + {{0xd8,0x59,0xaa,0x50,0xd9,0xe1,0x84,0x73,0x4c,0xd5,0x23,0xe2,0x3d,0x0f,0x1e,0xc7,0x1d,0x4b,0x56,0xcc},21545,12581,51,9701,6805,0,18459,0}, + {{0xd8,0x75,0xfe,0xdd,0x27,0xe1,0xee,0xa4,0x69,0x6f,0x68,0x61,0x87,0x30,0x37,0x45,0x11,0x0c,0x5a,0xf3},20092,7738,67,9986,6805,0,18459,0}, + {{0xd9,0x21,0x98,0xbb,0x9f,0x3a,0xb1,0x46,0x3b,0xa3,0xf3,0x54,0x4d,0x1b,0x35,0x7c,0x9c,0xda,0xd5,0xef},19386,4120,147,10782,16652,0,21138,0}, + {{0xda,0x07,0x34,0xab,0x6d,0x82,0xdd,0x0a,0xd9,0x36,0xbe,0x8b,0xa1,0xbc,0xe9,0x32,0x2d,0x1b,0xda,0x6a},18441,1589,67,7372,6805,0,21138,0}, + {{0xda,0x6a,0x48,0x26,0x29,0x6b,0xed,0x48,0x83,0x4a,0xde,0xda,0xd2,0x87,0x83,0xca,0x84,0x8d,0xa8,0xc8},20185,1757,67,4897,6805,0,20670,2}, + {{0xda,0xb9,0xe6,0x86,0x7e,0x39,0x2b,0xf0,0xfd,0xed,0xd8,0x34,0x73,0x48,0x67,0x8a,0x32,0x4e,0x19,0x86},20945,11637,46,9644,6805,0,18459,0}, + {{0xda,0xbb,0x3f,0x3e,0x9d,0x04,0xb5,0xdd,0xaa,0xf2,0x10,0xcc,0xd4,0xc7,0x66,0xae,0x6d,0x19,0xd1,0x98},20774,16243,72,6304,6805,0,21138,0}, + {{0xda,0xbc,0xd3,0xb1,0x9c,0x75,0x1a,0x23,0xa6,0x80,0x4e,0x79,0xe4,0x66,0xc0,0xe2,0xdc,0xcc,0x43,0x94},20366,8481,72,9701,6805,0,21138,0}, + {{0xdb,0xe5,0x1d,0xe4,0xcd,0x9c,0xfc,0x1b,0xbd,0x72,0x36,0xc5,0x13,0x78,0x89,0x22,0x0f,0xe2,0xa4,0x6a},20969,11769,51,2796,6805,0,18459,0}, + {{0xdc,0x0a,0xe1,0x07,0x10,0xaa,0x7b,0xa4,0xf4,0x9a,0x3e,0x26,0x06,0xd9,0x39,0x23,0x08,0x91,0xa3,0x40},20025,7944,77,9663,6805,0,20657,0}, + {{0xdc,0x0d,0x06,0xc1,0xb3,0x53,0x33,0x0e,0x0e,0xde,0x74,0xba,0x6a,0x5d,0xcb,0x8a,0x0d,0x12,0x54,0x80},19684,6696,62,752,6805,0,18459,0}, + {{0xdc,0x68,0x31,0x2f,0xf5,0xa4,0x5f,0xf6,0x6a,0xfe,0x23,0x16,0x3b,0xbd,0xd5,0x1d,0x9c,0x18,0x6b,0x18},22188,17594,72,9663,6805,0,21138,0}, + {{0xdd,0x66,0x93,0xf7,0xdb,0x94,0xb8,0x31,0xf3,0x92,0x1d,0xe0,0x14,0x56,0xf7,0x2e,0x43,0x23,0x36,0x5e},18860,3584,67,5921,16652,0,21138,0}, + {{0xdd,0x72,0x05,0x0d,0x2c,0xa5,0x3c,0x21,0x33,0x05,0x33,0x8e,0x24,0xbd,0x6c,0xcb,0xce,0x7e,0x20,0xf5},21918,16351,72,7471,6805,0,21138,0}, + {{0xde,0x8d,0x36,0xd8,0x8c,0xff,0x75,0x8d,0x94,0x33,0x39,0x0c,0x2e,0x6f,0xfc,0x12,0xc9,0xae,0x78,0xae},20475,4687,67,5914,6805,0,21138,0}, + {{0xde,0xdf,0xfa,0x3d,0x7b,0x3e,0x27,0x9e,0x4a,0x49,0x8a,0x9e,0xc4,0x27,0x86,0xee,0xd9,0x39,0x31,0x6c},19029,4374,67,11880,6805,0,21138,0}, + {{0xdf,0x09,0xe3,0x86,0x50,0x2d,0xc0,0x40,0xc1,0xfe,0xa8,0x60,0x25,0xa3,0xf7,0xda,0x4f,0x38,0xf6,0x82},21474,14212,72,11942,6805,0,20657,0}, + {{0xdf,0x13,0x72,0x87,0x22,0x21,0xb4,0x09,0xf1,0xf4,0x0f,0xe3,0xec,0xaa,0x2c,0x6f,0x36,0x73,0xb8,0x7f},18603,2679,67,5914,6805,0,21138,0}, + {{0xdf,0xa6,0xb1,0x21,0xfb,0x38,0xc2,0xb2,0x6a,0x96,0xbc,0x53,0xf8,0x6c,0xe0,0x9f,0x44,0x7f,0xbd,0xb5},21039,11961,51,10706,6805,0,18459,0}, + {{0xdf,0xa9,0xd9,0x9f,0x51,0xa5,0xc1,0x48,0x8e,0xeb,0x44,0x89,0xb1,0x2c,0x78,0x93,0x56,0x61,0xd7,0x37},18962,3963,62,9986,6805,18955,21138,0}, + {{0xdf,0xfb,0x91,0xae,0x82,0xff,0x96,0xa6,0x28,0x2a,0x9f,0x4d,0x9f,0x86,0x01,0x0c,0x24,0x1a,0x42,0xa4},21634,14593,72,11759,6805,0,21138,0}, + {{0xe0,0x52,0xde,0xea,0x6d,0x41,0x59,0x4b,0x45,0x42,0x0c,0x9d,0xc1,0xe4,0x67,0xb6,0x6d,0x3e,0x06,0x05},18586,2550,67,5914,6805,18579,21138,0}, + {{0xe0,0x5f,0x90,0x0c,0x71,0x35,0xde,0x67,0xb4,0x52,0x3f,0xcd,0x42,0x5e,0xc7,0x5c,0xac,0x5f,0x1c,0xf8},22208,17635,62,9701,6805,0,21138,0}, + {{0xe0,0xc0,0xc5,0x5c,0xa1,0x45,0xc2,0xe0,0xf1,0xa1,0xef,0xa7,0xa4,0xd5,0xf2,0xf5,0x9d,0x34,0xc9,0x13},18364,1404,72,9663,6805,0,20657,0}, + {{0xe1,0x92,0x18,0xe6,0xcd,0xc4,0x8c,0xcc,0xb4,0xf6,0x0b,0xdc,0xdd,0x92,0x35,0x0e,0x7a,0xbd,0xb5,0x29},18340,1333,62,9652,6805,0,21138,0}, + {{0xe2,0x8f,0xe7,0x8e,0x38,0x7b,0x4d,0x04,0xb0,0xd9,0xbf,0xff,0xc0,0xf7,0xa3,0x14,0x7a,0x67,0x67,0xcb},19480,5872,72,5914,6805,0,21138,0}, + {{0xe2,0xeb,0x89,0x43,0x14,0xc1,0xf5,0x31,0x04,0x90,0xa7,0xc0,0x16,0xca,0x7e,0x65,0xd4,0xd0,0x57,0x35},18525,2422,72,9986,6805,0,21138,0}, + {{0xe3,0x3c,0x00,0x0b,0x4a,0xef,0x2a,0x98,0x89,0xbb,0x6f,0xa2,0x4b,0xc7,0x62,0xc6,0xf5,0xc5,0xc5,0xc6},18543,2259,46,9986,6805,0,18459,0}, + {{0xe3,0xaa,0x7b,0x19,0x50,0xf7,0x0c,0x70,0xb0,0x28,0x64,0xad,0x31,0xd7,0x8e,0x7c,0x3b,0x38,0x99,0x64},18382,1460,62,9156,6805,0,21138,0}, + {{0xe4,0x16,0x70,0xfc,0x6c,0x48,0x12,0x86,0xd0,0x85,0x6a,0xbf,0x76,0xa3,0x2d,0x4a,0xca,0x65,0x4b,0x7d},18503,2188,51,5914,6805,0,18459,0}, + {{0xe4,0x6d,0xee,0xce,0x4f,0xd5,0x03,0x24,0x46,0xcb,0x8d,0xb4,0xa2,0xae,0xd8,0xb7,0x9c,0x86,0xa7,0x18},18949,3917,62,15641,16652,0,18459,0}, + {{0xe5,0x7c,0x40,0x58,0x7a,0x7e,0x5e,0x5f,0x53,0xc2,0x5b,0xfa,0x54,0x3c,0x0e,0xc2,0x99,0xfe,0x05,0xb3},19085,4638,72,9144,6805,0,21138,1}, + {{0xe6,0x36,0xfd,0xf8,0x33,0x93,0xa3,0x1b,0x2c,0x3f,0x64,0x00,0xd5,0x59,0xf4,0xa7,0x67,0x1c,0x3e,0x97},18471,1977,62,9644,6805,0,21138,0}, + {{0xe7,0x1f,0x52,0x74,0x09,0xa1,0x60,0x69,0x93,0xc0,0xde,0xcb,0xe3,0x1f,0x3e,0x34,0x05,0xc6,0x91,0xd3},20783,16276,62,6304,6805,0,21138,0}, + {{0xe7,0x83,0x93,0x36,0xfa,0xde,0xb7,0xfd,0x6a,0x7e,0x93,0x48,0x60,0x8e,0xc4,0xd2,0xe7,0x93,0x1d,0xf1},21483,14260,72,2225,6805,0,21138,0}, + {{0xe7,0x95,0xb4,0xb6,0xc1,0xcb,0x40,0xf7,0xa6,0x5b,0x6e,0xab,0xaf,0x70,0xb3,0x43,0x75,0x52,0x61,0x04},18519,9337,62,9986,6805,0,21138,2}, + {{0xe9,0x07,0x0e,0xa3,0xfc,0xc0,0xf2,0xd9,0x57,0x4e,0x40,0x51,0xf6,0xce,0x6c,0x82,0xb1,0x46,0x34,0xc6},19976,7822,46,1393,6805,0,18459,0}, + {{0xe9,0x07,0xf2,0xf6,0x37,0xee,0xd0,0xa1,0xf0,0xea,0x52,0x37,0x1f,0x51,0xf9,0xdc,0xb6,0x04,0x2a,0x6f},19092,4583,77,5957,6805,0,20657,0}, + {{0xe9,0xc2,0xf7,0xf7,0x2e,0x13,0xc6,0xed,0xb2,0xc0,0x68,0xa6,0xe8,0xdf,0xe0,0xe8,0x40,0x6c,0xf4,0xaf},20864,11319,51,9652,6805,0,18459,0}, + {{0xea,0xf2,0x5a,0xb9,0x0d,0x3a,0x28,0xd8,0xcc,0x74,0x2d,0xff,0x60,0x7c,0x1e,0x97,0x4c,0x07,0x9c,0x78},19767,6357,46,267,6805,0,21138,0}, + {{0xeb,0x21,0xd2,0x2c,0xd7,0x06,0x13,0x7f,0x0f,0xbb,0xa6,0x8d,0x6b,0xdc,0x8e,0x36,0xc7,0xcb,0xa7,0xa7},19627,6225,107,9289,16652,0,21138,0}, + {{0xeb,0x74,0xff,0xb4,0xbe,0xb5,0x92,0xd5,0xf6,0x43,0x55,0xe8,0x94,0x83,0x76,0xe8,0x5f,0x38,0x23,0xf3},21260,13183,62,17099,6805,21252,21138,0}, + {{0xec,0x1c,0x0c,0x2f,0x73,0xec,0xff,0x18,0x66,0x61,0xea,0xd3,0x13,0x48,0xd2,0xf3,0xdf,0x7a,0x70,0xe9},22268,17911,67,5914,6805,0,21138,1}, + {{0xed,0x89,0xec,0x85,0xee,0xf9,0x94,0x40,0x64,0xaa,0xe9,0x73,0x8a,0xad,0x51,0xb4,0xcc,0x6d,0x84,0xa6},19720,6900,67,9156,6805,0,21138,0}, + {{0xee,0x6e,0xbb,0xb0,0xea,0x52,0x72,0x5b,0xcd,0xdf,0x97,0x24,0x4d,0x67,0x18,0xe5,0x67,0x48,0x9e,0x32},20748,10948,77,9663,6805,0,21138,0}, + {{0xee,0xe1,0x8b,0x41,0x5d,0xd6,0xbf,0x4b,0x6c,0xbf,0x28,0x15,0xb6,0xc6,0x88,0xa3,0x82,0xb9,0x48,0x3a},19836,12006,41,9652,6805,0,18459,0}, + {{0xee,0xe5,0x7a,0x8a,0xbe,0x44,0x39,0x3c,0xe8,0x5d,0x55,0x94,0xbc,0xb7,0x53,0xcc,0xcf,0x79,0x8b,0xfd},21587,15097,67,9701,6805,0,21138,0}, + {{0xef,0x62,0xf7,0x9a,0x75,0xb8,0x31,0x37,0xf6,0x01,0x0f,0x2e,0xcc,0x29,0x28,0xa9,0x76,0xb8,0x5d,0x50},19791,12039,46,17120,6805,0,18459,0}, + {{0xef,0x88,0x9e,0xdb,0xa3,0x01,0xb2,0x6f,0x7b,0x33,0x29,0x4b,0xe7,0x9f,0x63,0xb6,0x05,0x03,0xb9,0x78},20067,8115,67,5933,16652,0,21138,0}, + {{0xf0,0xcd,0x99,0xe2,0x6f,0x88,0x13,0x58,0x9c,0xe0,0x9d,0xa7,0x4c,0x26,0xf4,0x90,0x0e,0x75,0x11,0x28},20438,8846,67,7471,6805,0,21138,0}, + {{0xf1,0xa0,0x76,0x19,0xa4,0xc2,0x5e,0x26,0x50,0xf6,0xd2,0x84,0x1a,0x3d,0xf5,0xea,0x1a,0xa6,0x3d,0x38},19863,12262,46,9652,6805,0,18459,0}, + {{0xf1,0xbd,0x5e,0xfb,0x7a,0x03,0x3e,0xa6,0x0d,0x69,0x52,0x03,0x8d,0xb8,0xcc,0x7d,0x49,0x88,0x93,0x8d},19550,10433,67,13847,6805,0,21138,0}, + {{0xf1,0xc3,0x40,0x18,0x9f,0x56,0x01,0x83,0xc4,0x06,0xd2,0xbf,0x7a,0xc5,0x85,0xaf,0xa6,0x04,0xc5,0xe0},22272,17962,67,5914,6805,22268,21138,1}, + {{0xf2,0x62,0x00,0x89,0x39,0xd5,0xc1,0xe1,0x76,0x1c,0xc8,0x28,0x56,0x22,0xeb,0xb0,0xad,0x85,0x6d,0x5d},19889,7207,72,9701,6805,0,21138,0}, + {{0xf3,0x13,0x23,0xd3,0x04,0x74,0xba,0x04,0x6b,0xa9,0x94,0x4c,0x3a,0xc3,0xc1,0x47,0x0b,0xc0,0xc1,0x26},232,187,72,9663,6805,0,20657,0}, + {{0xf3,0x55,0x99,0xf3,0xb4,0x0d,0xb2,0xf4,0xdf,0x9b,0xaf,0xbb,0xd5,0x97,0x70,0x0a,0x28,0xd8,0xfc,0x1e},19190,5048,62,5914,6805,0,21138,0}, + {{0xf3,0xcb,0xad,0x96,0x21,0xbb,0x8f,0x6f,0xf7,0x53,0xa2,0xb3,0xfe,0x0b,0xb0,0x30,0x82,0x55,0x67,0x27},18825,3328,72,9144,6805,0,21138,0}, + {{0xf4,0x20,0x63,0xd7,0xdc,0x0d,0xed,0x7b,0xf8,0xd2,0xb4,0x11,0x26,0x06,0x71,0x00,0x7a,0xae,0xdb,0xb0},21420,14042,62,7471,6805,21411,21138,0}, + {{0xf4,0x9d,0x23,0x97,0x9b,0x1c,0x26,0x25,0xe1,0x8a,0xb3,0x1c,0x74,0x2f,0x87,0x3d,0x58,0xfe,0x0b,0xc7},18432,17175,46,10706,6811,0,18459,0}, + {{0xf6,0xca,0x11,0x3b,0x18,0xbf,0x03,0x5d,0x24,0x38,0x85,0xd2,0x33,0x42,0xc8,0xb0,0x67,0x3d,0x11,0x8c},19454,13327,51,17099,6805,19430,18459,2}, + {{0xf7,0x64,0x8c,0x49,0x4b,0x02,0x5a,0xf2,0x36,0x12,0xad,0xec,0x90,0xc1,0x47,0x8e,0xa7,0x3f,0x79,0xa7},21104,12513,51,9701,6805,0,18459,0}, + {{0xf7,0x76,0xd7,0xb7,0xf8,0x02,0xd0,0xf3,0x14,0x1f,0x54,0x70,0x8d,0xe7,0x3d,0xc8,0x01,0x23,0x15,0x37},21218,12908,67,9947,6805,0,21138,0}, + {{0xf7,0x7c,0x0c,0x27,0x5c,0xf6,0xf5,0x64,0x84,0xe9,0x7f,0x1b,0x33,0xf2,0x82,0x7f,0xed,0x88,0x80,0x44},18887,1996,72,5914,6805,18879,21138,0}, + {{0xf7,0xe3,0x09,0xdd,0x40,0x48,0x6a,0xd2,0xd8,0xdb,0x6e,0x42,0xde,0xc0,0xe5,0x44,0x8e,0x9e,0x91,0xde},19693,6760,67,752,6805,0,21138,0}, + {{0xf7,0xe7,0xe3,0x6f,0x21,0xa5,0x80,0xb9,0xcf,0xda,0xfd,0xba,0x72,0xcd,0xc8,0x02,0xc4,0xa1,0x9f,0x3f},19800,12127,46,17120,6805,0,18459,0}, + {{0xf8,0x01,0x62,0x3e,0xd6,0xa6,0x39,0xe2,0xe3,0x1a,0x25,0x47,0x90,0x20,0x9f,0xa1,0xc8,0xdf,0xc7,0x4f},21095,1122,72,946,6805,0,21138,0}, + {{0xf8,0x53,0x28,0x87,0x41,0xbe,0x1e,0x74,0x9b,0x19,0x89,0x78,0x61,0xb0,0xf5,0x58,0xa3,0x37,0xda,0xc9},18465,1952,72,9652,6805,0,21138,0}, + {{0xf8,0x5b,0x3e,0xb1,0x49,0x00,0x56,0x16,0xd0,0x1f,0xd3,0xc7,0x27,0x4c,0xfa,0x7d,0x76,0xbe,0x95,0xdd},20900,11538,67,9701,6805,0,21138,0}, + {{0xf8,0xc7,0x55,0x7f,0xc9,0x4e,0x6a,0xe0,0xb1,0x0a,0x1a,0x4a,0x61,0xa4,0x41,0xe6,0x29,0xf8,0x30,0x45},19653,6161,62,6157,6805,0,18459,0}, + {{0xf9,0xfe,0xac,0xa7,0xfe,0x0e,0x2f,0x36,0x92,0x6b,0x5f,0x2e,0x28,0x8d,0x42,0x3f,0x63,0xbc,0x09,0xee},20430,9496,87,9701,6805,0,21138,0}, + {{0xfa,0x01,0xd9,0xa0,0x34,0x16,0xd3,0x5a,0x31,0x37,0x40,0x72,0x8a,0x79,0x7f,0x87,0x92,0xdd,0xfd,0xa1},22101,17070,72,5957,6805,0,20657,0}, + {{0xfa,0x4e,0xf8,0x34,0x17,0x82,0xc3,0x5f,0x52,0xfb,0xc0,0xe1,0xf7,0xaf,0x28,0xd5,0x81,0x62,0x9f,0x53},18551,2303,62,9986,16652,0,18459,0}, + {{0xfa,0x5d,0x54,0x3b,0xb0,0x9c,0x6b,0xa8,0x5a,0xa7,0xe3,0x49,0x49,0x2c,0x65,0x99,0xe5,0x98,0x82,0xb5},18560,2327,51,9986,6805,18551,18459,0}, + {{0xfa,0x9d,0x61,0x1e,0xdd,0x6d,0xb7,0xa4,0x5e,0x71,0xd0,0xd8,0x44,0x28,0xf9,0x0e,0xd5,0x54,0x0a,0x19},18652,3161,67,9701,6805,0,21138,2}, + {{0xfc,0x40,0xac,0xf0,0x74,0x5a,0x62,0x7e,0x54,0xb8,0xa2,0x51,0x49,0x75,0xd7,0x24,0x59,0xb1,0xea,0x0b},18218,305,77,4897,6805,0,20670,2}, + {{0xfc,0xe1,0xf8,0xfc,0x9f,0xd8,0x2e,0x7b,0x91,0x81,0x78,0x45,0x08,0x46,0xe1,0xfc,0x7e,0x6d,0x43,0x2e},20697,8347,72,9663,6805,0,20657,2}, + {{0xfd,0x59,0xee,0xd6,0x9f,0x05,0x21,0x54,0x46,0x17,0xc3,0x6b,0x5f,0xee,0x1b,0x71,0x7a,0x06,0xe9,0xf4},19559,10467,72,13847,6805,0,21138,0}, + {{0xfd,0x6a,0x1a,0xb3,0x70,0x73,0x86,0xd6,0x03,0xa4,0x6b,0x07,0x69,0xa8,0x2f,0xd7,0x74,0x0a,0xb3,0x93},22224,17703,67,9701,6805,0,21138,0}, + {{0xfd,0x95,0xce,0x97,0xa9,0xfa,0x8b,0x24,0x9f,0xfb,0x41,0x91,0x10,0xa1,0xc1,0x81,0x71,0x77,0x2c,0x36},18358,1355,67,5933,16652,0,21138,0}, + {{0xfd,0xd2,0xbc,0x51,0xe3,0x34,0x20,0x93,0xdd,0x39,0x5b,0xec,0xd9,0xa2,0x13,0x21,0xde,0xc8,0x89,0x4b},19644,6295,117,426,16652,0,21138,0}, + {{0xfd,0xf1,0x4d,0xa4,0x1b,0xad,0x6c,0x4f,0xc4,0x29,0x4d,0x58,0xc3,0x24,0xab,0x96,0xc9,0x60,0x5e,0x3a},19923,7385,67,7457,6805,0,21138,0}, + {{0xfe,0x37,0x3c,0xbd,0xca,0xb0,0xcd,0x8e,0x4f,0x63,0xb0,0x3c,0x6c,0x52,0xb6,0xd6,0x53,0xa5,0x8f,0x7a},22085,8565,51,9947,6805,0,18459,0}, + {{0xfe,0x84,0xe8,0xc9,0x43,0x8f,0xaf,0xd6,0xa4,0x99,0xcb,0x10,0x7b,0xab,0x73,0xc5,0x71,0x4f,0x24,0xaa},22301,18013,67,946,6805,0,21138,0}, + {{0xfe,0xcc,0x69,0xfc,0xae,0x81,0x09,0x2c,0x52,0x16,0x01,0x41,0x87,0xab,0x84,0xce,0xdd,0x36,0x59,0xfb},18510,2240,62,2225,6805,0,18459,0}, + {{0xff,0x12,0xd9,0xf1,0xf4,0x62,0x63,0xdc,0xcd,0x49,0xdd,0x8a,0xdb,0x6a,0x55,0x55,0xba,0x7f,0x85,0x56},19959,7593,67,5957,6805,0,21138,0}, + {{0xff,0x2c,0xf1,0x7d,0xc8,0x74,0x19,0xb0,0x51,0xa6,0xdb,0x09,0xa4,0xf5,0x92,0x00,0x7a,0x13,0xb5,0x8b},22042,9519,62,9986,6805,0,21138,0}, +}; + +static const UINT32 pceCdGameCount = 504; +static const char pceCdSoftwareListDescription[] = "NEC PC Engine / Turbografx-16 CD-ROMs"; + +#endif diff --git a/src/burner/qt/meson.build b/src/burner/qt/meson.build index e9a5b51c4..078be6401 100644 --- a/src/burner/qt/meson.build +++ b/src/burner/qt/meson.build @@ -27,7 +27,8 @@ frontend_sources = files( 'logdialog.cpp', 'main.cpp', 'mainwindow.cpp', - 'neocdlist.cpp', + '../cdlist.cpp', + '../../intf/cd/cd_img.cpp', 'oglviewport.cpp', 'progress.cpp', 'qaudiointerface.cpp', diff --git a/src/burner/qt/neocdlist.cpp b/src/burner/qt/neocdlist.cpp deleted file mode 100644 index bfa6457f6..000000000 --- a/src/burner/qt/neocdlist.cpp +++ /dev/null @@ -1,393 +0,0 @@ -// --------------------------------------------------------------------------------------- -// NeoGeo CD Game Info Module (by CaptainCPS-X) -// --------------------------------------------------------------------------------------- -#include "burner.h" -#include "../neocdlist.h" -#include "../neocdlist_games.h" - -NGCDGAME* GetNeoGeoCDInfo(unsigned int nID) -{ - for(unsigned int nGame = 0; nGame < (sizeof(games) / sizeof(NGCDGAME)); nGame++) { - if(nID == games[nGame].id ) { - return &games[nGame]; - } - } - - return NULL; -} - -NGCDGAME* game; - -// Get the title -int GetNeoCDTitle(unsigned int nGameID) -{ - game = (NGCDGAME*)malloc(sizeof(NGCDGAME)); - memset(game, 0, sizeof(NGCDGAME)); - - if(GetNeoGeoCDInfo(nGameID)) - { - memcpy(game, GetNeoGeoCDInfo(nGameID), sizeof(NGCDGAME)); - return 1; - } - - game = NULL; - return 0; -} - -void iso9660_ReadOffset(unsigned char *Dest, FILE* fp, unsigned int lOffset, unsigned int lSize, unsigned int lLength) -{ - if(fp == NULL) return; - if (Dest == NULL) return; - - fseek(fp, lOffset, SEEK_SET); - fread(Dest, lLength, lSize, fp); -} - -static void NeoCDList_iso9660_CheckDirRecord(FILE* fp, int nSector) -{ - int nSectorLength = 2048; - //int nFile = 0; - unsigned int lBytesRead = 0; - //int nLen = 0; - unsigned int lOffset = 0; - bool bNewSector = false; - bool bRevisionQueve = false; - int nRevisionQueveID = 0; - - lOffset = (nSector * nSectorLength); - - unsigned char* nLenDR = (unsigned char*)malloc(1 * sizeof(unsigned char)); - unsigned char* Flags = (unsigned char*)malloc(1 * sizeof(unsigned char)); - unsigned char* ExtentLoc = (unsigned char*)malloc(8 * sizeof(unsigned char)); - unsigned char* Data = (unsigned char*)malloc(0x10b * sizeof(unsigned char)); - unsigned char* LEN_FI = (unsigned char*)malloc(1 * sizeof(unsigned char)); - char *File = (char*)malloc(32 * sizeof(char)); - - while(1) - { - iso9660_ReadOffset(nLenDR, fp, lOffset, 1, sizeof(unsigned char)); - - if(nLenDR[0] == 0x22) { - lOffset += nLenDR[0]; - lBytesRead += nLenDR[0]; - continue; - } - - if(nLenDR[0] < 0x22) - { - if(bNewSector) - { - if(bRevisionQueve) { - bRevisionQueve = false; - - GetNeoCDTitle(nRevisionQueveID); - } - return; - } - - nLenDR[0] = 0; - iso9660_ReadOffset(nLenDR, fp, lOffset + 1, 1, sizeof(unsigned char)); - - if(nLenDR[0] < 0x22) { - lOffset += (2048 - lBytesRead); - lBytesRead = 0; - bNewSector = true; - continue; - } - } - - bNewSector = false; - - iso9660_ReadOffset(Flags, fp, lOffset + 25, 1, sizeof(unsigned char)); - - if(!(Flags[0] & (1 << 1))) - { - iso9660_ReadOffset(ExtentLoc, fp, lOffset + 2, 8, sizeof(unsigned char)); - - char szValue[9]; - sprintf(szValue, "%02x%02x%02x%02x", ExtentLoc[4], ExtentLoc[5], ExtentLoc[6], ExtentLoc[7]); - - unsigned int nValue = 0; - sscanf(szValue, "%x", &nValue); - - iso9660_ReadOffset(Data, fp, nValue * 2048, 0x10a, sizeof(unsigned char)); - - char szData[8]; - sprintf(szData, "%c%c%c%c%c%c%c", Data[0x100], Data[0x101], Data[0x102], Data[0x103], Data[0x104], Data[0x105], Data[0x106]); - - if(!strncmp(szData, "NEO-GEO", 7)) - { - char id[] = "0000"; - sprintf(id, "%02X%02X", Data[0x108], Data[0x109]); - - unsigned int nID = 0; - sscanf(id, "%x", &nID); - - iso9660_ReadOffset(LEN_FI, fp, lOffset + 32, 1, sizeof(unsigned char)); - - iso9660_ReadOffset((unsigned char*)File, fp, lOffset + 33, LEN_FI[0], sizeof(char)); - strncpy(File, File, LEN_FI[0]); - File[LEN_FI[0]] = 0; - - // King of Fighters '94, The (1994)(SNK)(JP) - // 10-6-1994 (P1.PRG) - if(nID == 0x0055 && Data[0x67] == 0xDE) { - // ...continue - } - - // King of Fighters '94, The (1994)(SNK)(JP-US) - // 11-21-1994 (P1.PRG) - if(nID == 0x0055 && Data[0x67] == 0xE6) { - // Change to custom revision id - nID = 0x1055; - } - - // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B01, B03-B06, NGCD-084E MT B01] - // 9-11-1995 (P1.PRG) - if(nID == 0x0084 && Data[0x6C] == 0xC0) { - // ... continue - } - - // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B10, NGCD-084E MT B03] - // 10-5-1995 (P1.PRG) - if(nID == 0x0084 && Data[0x6C] == 0xFF) { - // Change to custom revision id - nID = 0x1084; - } - - // King of Fighters '96 NEOGEO Collection, The - if(nID == 0x0229) { - bRevisionQueve = false; - - GetNeoCDTitle(nID); - break; - } - - // King of Fighters '96, The - if(nID == 0x0214) { - bRevisionQueve = true; - nRevisionQueveID = nID; - lOffset += nLenDR[0]; - lBytesRead += nLenDR[0]; - continue; - } - - GetNeoCDTitle(nID); - - break; - } - } - - lOffset += nLenDR[0]; - lBytesRead += nLenDR[0]; - } - - if (nLenDR) { - free(nLenDR); - nLenDR = NULL; - } - - if (Flags) { - free(Flags); - Flags = NULL; - } - - if (ExtentLoc) { - free(ExtentLoc); - ExtentLoc = NULL; - } - - if (Data) { - free(Data); - Data = NULL; - } - - if (LEN_FI) { - free(LEN_FI); - LEN_FI = NULL; - } - - if (File) { - free(File); - File = NULL; - } -} - -// Check the specified ISO, and proceed accordingly -static int NeoCDList_CheckISO(TCHAR* pszFile) -{ - if(!pszFile) { - // error - return 0; - } - - // Make sure we have a valid ISO file extension... - if(_tcsstr(pszFile, _T(".iso")) || _tcsstr(pszFile, _T(".ISO")) ) - { - FILE* fp = _tfopen(pszFile, _T("rb")); - if(fp) - { - // Read ISO and look for 68K ROM standard program header, ID is always there - // Note: This function works very quick, doesn't compromise performance :) - // it just read each sector first 264 bytes aproximately only. - - // Get ISO Size (bytes) - fseek(fp, 0, SEEK_END); - unsigned int lSize = 0; - lSize = ftell(fp); - //rewind(fp); - fseek(fp, 0, SEEK_SET); - - // If it has at least 16 sectors proceed - if(lSize > (2048 * 16)) - { - // Check for Standard ISO9660 Identifier - unsigned char IsoHeader[2048 * 16 + 1]; - unsigned char IsoCheck[6]; - - fread(IsoHeader, 1, 2048 * 16 + 1, fp); // advance to sector 16 and PVD Field 2 - fread(IsoCheck, 1, 5, fp); // get Standard Identifier Field from PVD - - // Verify that we have indeed a valid ISO9660 MODE1/2048 - if(!memcmp(IsoCheck, "CD001", 5)) - { - //bprintf(PRINT_NORMAL, _T(" Standard ISO9660 Identifier Found. \n")); - iso9660_VDH vdh; - - // Get Volume Descriptor Header - memset(&vdh, 0, sizeof(vdh)); - //memcpy(&vdh, iso9660_ReadOffset(fp, (2048 * 16), sizeof(vdh)), sizeof(vdh)); - iso9660_ReadOffset((unsigned char*)&vdh, fp, 2048 * 16, 1, sizeof(vdh)); - - // Check for a valid Volume Descriptor Type - if(vdh.vdtype == 0x01) - { -#if 0 -// This will fail on 64-bit due to differing variable sizes in the pvd struct - // Get Primary Volume Descriptor - iso9660_PVD pvd; - memset(&pvd, 0, sizeof(pvd)); - //memcpy(&pvd, iso9660_ReadOffset(fp, (2048 * 16), sizeof(pvd)), sizeof(pvd)); - iso9660_ReadOffset((unsigned char*)&pvd, fp, 2048 * 16, 1, sizeof(pvd)); - - // ROOT DIRECTORY RECORD - - // Handle Path Table Location - char szRootSector[32]; - unsigned int nRootSector = 0; - - sprintf(szRootSector, "%02X%02X%02X%02X", - pvd.root_directory_record.location_of_extent[4], - pvd.root_directory_record.location_of_extent[5], - pvd.root_directory_record.location_of_extent[6], - pvd.root_directory_record.location_of_extent[7]); - - // Convert HEX string to Decimal - sscanf(szRootSector, "%X", &nRootSector); -#else -// Just read from the file directly at the correct offset (0x8000 + 0x9e for the start of the root directory record) - unsigned char buffer[8]; - char szRootSector[4]; - unsigned int nRootSector = 0; - - fseek(fp, 0x809e, SEEK_SET); - fread(buffer, 1, 8, fp); - - sprintf(szRootSector, "%02x%02x%02x%02x", buffer[4], buffer[5], buffer[6], buffer[7]); - - sscanf(szRootSector, "%x", &nRootSector); -#endif - - // Process the Root Directory Records - NeoCDList_iso9660_CheckDirRecord(fp, nRootSector); - - // Path Table Records are not processed, since NeoGeo CD should not have subdirectories - // ... - } - } else { - - //bprintf(PRINT_NORMAL, _T(" Standard ISO9660 Identifier Not Found, cannot continue. \n")); - return 0; - } - } - fclose(fp); - } else { - - //bprintf(PRINT_NORMAL, _T(" Couldn't open %s \n"), GetIsoPath()); - return 0; - } - } else { - - //bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [ .iso / .ISO ] \n")); - return 0; - } - - return 1; -} - -// This will do everything -int GetNeoGeoCD_Identifier() -{ - if(!GetIsoPath() || !IsNeoGeoCD()) { - return 0; - } - - // Make sure we have a valid ISO file extension... - if(_tcsstr(GetIsoPath(), _T(".iso")) || _tcsstr(GetIsoPath(), _T(".ISO")) ) - { - if(_tfopen(GetIsoPath(), _T("rb"))) - { - // Read ISO and look for 68K ROM standard program header, ID is always there - // Note: This function works very quick, doesn't compromise performance :) - // it just read each sector first 264 bytes aproximately only. - NeoCDList_CheckISO(GetIsoPath()); - - } else { - - bprintf(PRINT_NORMAL, _T(" Couldn't open %s \n"), GetIsoPath()); - return 0; - } - - } else { - - bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [ .iso / .ISO ] \n")); - return 0; - } - - return 1; -} - -int NeoCDInfo_Init() -{ - NeoCDInfo_Exit(); - return GetNeoGeoCD_Identifier(); -} - -TCHAR* NeoCDInfo_Text(int nText) -{ - if(!game || !IsNeoGeoCD() || !bDrvOkay) return NULL; - - switch(nText) - { - case DRV_NAME: return game->pszName; - case DRV_FULLNAME: return game->pszTitle; - case DRV_MANUFACTURER: return game->pszCompany; - case DRV_DATE: return game->pszYear; - } - - return NULL; -} - -int NeoCDInfo_ID() -{ - if(!game || !IsNeoGeoCD() || !bDrvOkay) return 0; - return game->id; -} - -void NeoCDInfo_Exit() -{ - if(game) { - free(game); - game = NULL; - } -} diff --git a/src/burner/sdl/meson.build b/src/burner/sdl/meson.build index 7b84059c2..1fcd3a4ed 100644 --- a/src/burner/sdl/meson.build +++ b/src/burner/sdl/meson.build @@ -8,7 +8,7 @@ frontend_sources = files( 'localise_gamelist.cpp', 'main.cpp', 'media.cpp', - 'neocdlist.cpp', + '../cdlist.cpp', 'replay.cpp', 'romdata.cpp', 'run.cpp', diff --git a/src/burner/sdl/neocdlist.cpp b/src/burner/sdl/neocdlist.cpp deleted file mode 100644 index 7dffd5a94..000000000 --- a/src/burner/sdl/neocdlist.cpp +++ /dev/null @@ -1,409 +0,0 @@ -// --------------------------------------------------------------------------------------- -// NeoGeo CD Game Info Module (by CaptainCPS-X) -// --------------------------------------------------------------------------------------- -#include "burner.h" -#include "neocdlist.h" - -const int nSectorLength = 2352; - -NGCDGAME* game; - -#include "neocdlist_games.h" - -NGCDGAME* GetNeoGeoCDInfo(unsigned int nID) -{ - for (unsigned int nGame = 0; nGame < (sizeof(games) / sizeof(NGCDGAME)); nGame++) { - if (nID == games[nGame].id) { - return &games[nGame]; - } - } - - return NULL; -} - -// Update the main window title -static void SetNeoCDTitle(TCHAR* pszTitle) -{ - TCHAR szText[1024] = _T(""); - _stprintf(szText, _T(APP_TITLE) _T(" v%.20s") _T(SEPERATOR_1) _T("%s") _T(SEPERATOR_1) _T("%s"), szAppBurnVer, BurnDrvGetText(DRV_FULLNAME), pszTitle); - - // SetWindowText(hScrnWnd, szText); -} - -void NeoCDInfo_SetTitle() -{ - if (IsNeoGeoCD() && game && game->id) { - SetNeoCDTitle(game->pszTitle); - } - else if (IsNeoGeoCD()) { - SetNeoCDTitle("Unknown CD"); - } -} - -// Get the title -int GetNeoCDTitle(unsigned int nGameID) -{ - game = (NGCDGAME*)malloc(sizeof(NGCDGAME)); - memset(game, 0, sizeof(NGCDGAME)); - - if (GetNeoGeoCDInfo(nGameID)) { - memcpy(game, GetNeoGeoCDInfo(nGameID), sizeof(NGCDGAME)); - - bprintf(PRINT_NORMAL, _T(" Title: %s \n"), game->pszTitle); - bprintf(PRINT_NORMAL, _T(" Shortname: %s \n"), game->pszName); - bprintf(PRINT_NORMAL, _T(" Year: %s \n"), game->pszYear); - bprintf(PRINT_NORMAL, _T(" Company: %s \n"), game->pszCompany); - - // Update the main window title - //SetNeoCDTitle(game->pszTitle); - - return 1; - } - else { - //SetNeoCDTitle(FBALoadStringEx(hAppInst, IDS_UNIDENTIFIED_CD, true)); - } - - return 0; -} - -void iso9660_ReadOffset(UINT8* Dest, FILE* fp, unsigned int lOffset, unsigned int lSize, unsigned int lLength) -{ - if (fp == NULL || Dest == NULL) return; - - fseek(fp, lOffset + ((nSectorLength == 2352) ? 16 : 0), SEEK_SET); - fread(Dest, lLength, lSize, fp); -} - -static void NeoCDList_iso9660_CheckDirRecord(FILE* fp, int nSector) -{ - unsigned int lBytesRead = 0; - unsigned int lOffset = 0; - bool bNewSector = false; - bool bRevisionQueve = false; - int nRevisionQueveID = 0; - - lOffset = (nSector * nSectorLength); - - UINT8 nLenDR; - UINT8 Flags; - UINT8 LEN_FI; - UINT8* ExtentLoc = (UINT8*)malloc(8 + 32); - UINT8* Data = (UINT8*)malloc(0x10b + 32); - char* File = (char*)malloc(32 + 32); - - while (1) - { - iso9660_ReadOffset(&nLenDR, fp, lOffset, 1, sizeof(UINT8)); - - if (nLenDR == 0x22) { - lOffset += nLenDR; - lBytesRead += nLenDR; - continue; - } - - if (nLenDR < 0x22) - { - if (bNewSector) - { - if (bRevisionQueve) { - bRevisionQueve = false; - GetNeoCDTitle(nRevisionQueveID); - } - return; - } - - nLenDR = 0; - iso9660_ReadOffset(&nLenDR, fp, lOffset + 1, 1, sizeof(UINT8)); - - if (nLenDR < 0x22) { - lOffset += (nSectorLength - lBytesRead); - lBytesRead = 0; - bNewSector = true; - continue; - } - } - - bNewSector = false; - - iso9660_ReadOffset(&Flags, fp, lOffset + 25, 1, sizeof(UINT8)); - - if (!(Flags & (1 << 1))) - { - iso9660_ReadOffset(ExtentLoc, fp, lOffset + 2, 8, sizeof(UINT8)); - - char szValue[32]; - sprintf(szValue, "%02x%02x%02x%02x", ExtentLoc[4], ExtentLoc[5], ExtentLoc[6], ExtentLoc[7]); - - unsigned int nValue = 0; - sscanf(szValue, "%x", &nValue); - - iso9660_ReadOffset(Data, fp, nValue * nSectorLength, 0x10a, sizeof(UINT8)); - - char szData[32]; - sprintf(szData, "%c%c%c%c%c%c%c", Data[0x100], Data[0x101], Data[0x102], Data[0x103], Data[0x104], Data[0x105], Data[0x106]); - - if (!strncmp(szData, "NEO-GEO", 7)) - { - char id[32]; - sprintf(id, "%02X%02X", Data[0x108], Data[0x109]); - - unsigned int nID = 0; - sscanf(id, "%x", &nID); - - iso9660_ReadOffset(&LEN_FI, fp, lOffset + 32, 1, sizeof(UINT8)); - - iso9660_ReadOffset((UINT8*)File, fp, lOffset + 33, LEN_FI, sizeof(UINT8)); - strncpy(File, File, LEN_FI); - File[LEN_FI] = 0; - - // King of Fighters '94, The (1994)(SNK)(JP) - // 10-6-1994 (P1.PRG) - if (nID == 0x0055 && Data[0x67] == 0xDE) { - // ...continue - } - - // King of Fighters '94, The (1994)(SNK)(JP-US) - // 11-21-1994 (P1.PRG) - if (nID == 0x0055 && Data[0x67] == 0xE6) { - // Change to custom revision id - nID = 0x1055; - } - - // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B01, B03-B06, NGCD-084E MT B01] - // 9-11-1995 (P1.PRG) - if (nID == 0x0084 && Data[0x6C] == 0xC0) { - // ... continue - } - - // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B10, NGCD-084E MT B03] - // 10-5-1995 (P1.PRG) - if (nID == 0x0084 && Data[0x6C] == 0xFF) { - // Change to custom revision id - nID = 0x1084; - } - - // King of Fighters '96 NEOGEO Collection, The - if (nID == 0x0229) { - bRevisionQueve = false; - - GetNeoCDTitle(nID); - break; - } - - // King of Fighters '96, The - if (nID == 0x0214) { - bRevisionQueve = true; - nRevisionQueveID = nID; - lOffset += nLenDR; - lBytesRead += nLenDR; - continue; - } - - GetNeoCDTitle(nID); - break; - } - } - - lOffset += nLenDR; - lBytesRead += nLenDR; - } - - if (ExtentLoc) { - free(ExtentLoc); - ExtentLoc = NULL; - } - - if (Data) { - free(Data); - Data = NULL; - } - - if (File) { - free(File); - File = NULL; - } -} - -// Check the specified ISO, and proceed accordingly -static int NeoCDList_CheckISO(TCHAR* pszFile) -{ - if (!pszFile) { - // error - return 0; - } - - // Make sure we have a valid ISO file extension... - if (IsFileExt(pszFile, _T(".img")) || IsFileExt(pszFile, _T(".bin"))) - { - FILE* fp = _tfopen(pszFile, _T("rb")); - if (fp) - { - // Read ISO and look for 68K ROM standard program header, ID is always there - // Note: This function works very quick, doesn't compromise performance :) - // it just read each sector first 264 bytes aproximately only. - - // Get ISO Size (bytes) - fseek(fp, 0, SEEK_END); - unsigned int lSize = 0; - lSize = ftell(fp); - fseek(fp, 0, SEEK_SET); - - // If it has at least 16 sectors proceed - if (lSize > (nSectorLength * 16)) - { - // Check for Standard ISO9660 Identifier - UINT8 IsoCheck[32]; - - // advance to sector 16 and PVD Field 2 - iso9660_ReadOffset(&IsoCheck[0], fp, nSectorLength * 16 + 1, 1, 5); // get Standard Identifier Field from PVD - - // Verify that we have indeed a valid ISO9660 MODE1/2352 - if (!memcmp(IsoCheck, "CD001", 5)) - { - //bprintf(PRINT_NORMAL, _T(" Standard ISO9660 Identifier Found. \n")); - iso9660_VDH vdh; - - // Get Volume Descriptor Header - memset(&vdh, 0, sizeof(vdh)); - //memcpy(&vdh, iso9660_ReadOffset(fp, (2048 * 16), sizeof(vdh)), sizeof(vdh)); - iso9660_ReadOffset((UINT8*)&vdh, fp, 16 * nSectorLength, 1, sizeof(vdh)); - - // Check for a valid Volume Descriptor Type - if (vdh.vdtype == 0x01) - { -#if 0 - // This will fail on 64-bit due to differing variable sizes in the pvd struct - // Get Primary Volume Descriptor - iso9660_PVD pvd; - memset(&pvd, 0, sizeof(pvd)); - //memcpy(&pvd, iso9660_ReadOffset(fp, (2048 * 16), sizeof(pvd)), sizeof(pvd)); - iso9660_ReadOffset((UINT8*)&pvd, fp, nSectorLength * 16, 1, sizeof(pvd)); - - // ROOT DIRECTORY RECORD - - // Handle Path Table Location - char szRootSector[32]; - unsigned int nRootSector = 0; - - sprintf(szRootSector, "%02X%02X%02X%02X", - pvd.root_directory_record.location_of_extent[4], - pvd.root_directory_record.location_of_extent[5], - pvd.root_directory_record.location_of_extent[6], - pvd.root_directory_record.location_of_extent[7]); - - // Convert HEX string to Decimal - sscanf(szRootSector, "%X", &nRootSector); -#else - // Just read from the file directly at the correct offset (SECTOR_SIZE * 16 + 0x9e for the start of the root directory record) - UINT8 buffer[32]; - char szRootSector[32]; - unsigned int nRootSector = 0; - - iso9660_ReadOffset(&buffer[0], fp, nSectorLength * 16 + 0x9e, 1, 8); - - sprintf(szRootSector, "%02x%02x%02x%02x", buffer[4], buffer[5], buffer[6], buffer[7]); - - sscanf(szRootSector, "%x", &nRootSector); -#endif - - // Process the Root Directory Records - NeoCDList_iso9660_CheckDirRecord(fp, nRootSector); - - // Path Table Records are not processed, since NeoGeo CD should not have subdirectories - // ... - } - } - else { - - //bprintf(PRINT_NORMAL, _T(" Standard ISO9660 Identifier Not Found, cannot continue. \n")); - return 0; - } - } - } - else { - - //bprintf(PRINT_NORMAL, _T(" Couldn't open %s \n"), GetIsoPath()); - return 0; - } - - if (fp) fclose(fp); - - } - else { - - //bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [ .iso / .ISO ] \n")); - return 0; - } - - return 1; -} - -// This will do everything -int GetNeoGeoCD_Identifier() -{ - if (!GetIsoPath() || !IsNeoGeoCD()) { - return 0; - } - - // Make sure we have a valid ISO file extension... - if (IsFileExt(GetIsoPath(), _T(".img")) || IsFileExt(GetIsoPath(), _T(".bin"))) - { - if (_tfopen(GetIsoPath(), _T("rb"))) - { - bprintf(0, _T("NeoCDList: checking %s\n"), GetIsoPath()); - // Read ISO and look for 68K ROM standard program header, ID is always there - // Note: This function works very quick, doesn't compromise performance :) - // it just read each sector first 264 bytes aproximately only. - NeoCDList_CheckISO(GetIsoPath()); - - } - else { - - bprintf(PRINT_NORMAL, _T(" Couldn't open %s \n"), GetIsoPath()); - return 0; - } - - } - else { - - bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [ .img / .bin ] \n")); - return 0; - } - - return 1; -} - -int NeoCDInfo_Init() -{ - NeoCDInfo_Exit(); - return GetNeoGeoCD_Identifier(); -} - -TCHAR* NeoCDInfo_Text(int nText) -{ - if (!game || !IsNeoGeoCD()) return NULL; - - switch (nText) - { - case DRV_NAME: return game->pszName; - case DRV_FULLNAME: return game->pszTitle; - case DRV_MANUFACTURER: return game->pszCompany; - case DRV_DATE: return game->pszYear; - } - - return NULL; -} - -int NeoCDInfo_ID() -{ - if (!game || !IsNeoGeoCD()) return 0; - return game->id; -} - -void NeoCDInfo_Exit() -{ - if (game) { - free(game); - game = NULL; - } -} diff --git a/src/burner/win32/burner_win32.h b/src/burner/win32/burner_win32.h index 60e17f6d7..0194280a9 100644 --- a/src/burner/win32/burner_win32.h +++ b/src/burner/win32/burner_win32.h @@ -403,9 +403,6 @@ HBITMAP PNGLoadBitmap(HWND hWnd, FILE* fp, int nWidth, int nHeight, int nPreset) HBITMAP PNGLoadBitmapBuffer(HWND hWnd, void *buffer, int bufferLength, int nWidth, int nHeight, int nPreset); HBITMAP LoadBitmap(HWND hWnd, FILE* fp, int nWidth, int nHeight, int nPreset); int NeoCDList_CheckISO(TCHAR* pszFile, void (*pfEntryCallBack)(INT32, TCHAR*)); -TCHAR* ParseCueGetImageFile(const TCHAR* cueFullPath); -void FreeNGCDGame(NGCDGAME** ppGame); -INT32 GetNGCDGameTitle(const UINT32 nGameID, NGCDGAME** ppOutGame, bool bPrintLog = false); #include "cd_img.h" // romdata.cpp diff --git a/src/burner/win32/meson.build b/src/burner/win32/meson.build index 3b1f3366e..dcb5d65ea 100644 --- a/src/burner/win32/meson.build +++ b/src/burner/win32/meson.build @@ -60,7 +60,7 @@ frontend_sources = files( 'memcard.cpp', 'menu.cpp', 'misc_win32.cpp', - 'neocdlist.cpp', + '../cdlist.cpp', 'neocdsel.cpp', 'numdial.cpp', 'paletteviewer.cpp', @@ -69,7 +69,6 @@ frontend_sources = files( 'progress.cpp', 'replay.cpp', 'res.cpp', - 'romdata.cpp', 'roms.cpp', 'run.cpp', 'scrn.cpp', diff --git a/src/burner/win32/neocdlist.cpp b/src/burner/win32/neocdlist.cpp deleted file mode 100644 index 28d3954e7..000000000 --- a/src/burner/win32/neocdlist.cpp +++ /dev/null @@ -1,680 +0,0 @@ -// --------------------------------------------------------------------------------------- -// NeoGeo CD Game Info Module (by CaptainCPS-X) -// --------------------------------------------------------------------------------------- -#include "burner.h" -#include "neocdlist.h" -#include "burnint.h" -#include "cd_chd.h" - -#ifdef BUILD_NEOGEO -#define DEBUG_CD 0 - -const INT32 nSectorLength = 2352; - -static NGCDGAME* game; - -#include "neocdlist_games.h" - -// Distinguish between plain file and CHD I/O types -enum IO_TYPE { - IO_TYPE_NONE = 0, - IO_TYPE_FILE, - IO_TYPE_CHD -}; - -union IO_UNION { - FILE *pFile; // plain file handle - ChdImage *pChd; // CHD image (cd_chd module) -}; - -struct ISO_CTX { - enum IO_TYPE ioType; // current I/O type - union IO_UNION ioUnion; // I/O handle union - char szVolumeID[64]; // disc volume label (moved from former global) - INT32 bValid; // context validity flag - - // Full-image common metadata (parsed once, reused throughout) - UINT32 sectorSize; // bytes per sector - UINT32 totalSectors; // total image sectors -}; - -struct IO_STRATEGY -{ - enum IO_TYPE ioType; - // Open image, initialize context - INT32 (*fnOpen )(struct ISO_CTX* pIsoCtx, TCHAR* pszFile); - // Close image, release resources - void (*fnClose)(struct ISO_CTX* pIsoCtx); - // Read data by offset - void (*fnRead )(struct ISO_CTX* pIsoCtx, UINT8* Dest, UINT32 lOffset, UINT32 lSize, UINT32 lLength); -}; - -// TCHAR safe string duplicate (ANSI / Unicode auto-adaptive) -// Allocates heap memory and copies source TCHAR string safely -// Allocated memory must be released by free() or free_s() -static TCHAR* _tcsdup_s(const TCHAR* pSrc) -{ - if (IsStrEmpty(pSrc)) - return NULL; - - size_t srcLen = _tcslen(pSrc); - size_t allocSize = (srcLen + 1) * sizeof(TCHAR); - TCHAR* pDst = (TCHAR*)malloc(allocSize); - - if (!pDst) - return NULL; - - _tcsncpy(pDst, pSrc, srcLen); - pDst[srcLen] = _T('\0'); - - return pDst; -} - -static void iso9660_ReadOffset(FILE* fp, UINT8* Dest, UINT32 lOffset, UINT32 lSize, UINT32 lLength) -{ - if (!fp || !Dest) return; - fseek(fp, lOffset + ((nSectorLength == 2352) ? 16 : 0), SEEK_SET); - fread(Dest, lLength, lSize, fp); -} - -static INT32 FileIO_Open(struct ISO_CTX* pIsoCtx, TCHAR* pszFile) -{ - if (!pIsoCtx || !pszFile) return 0; - - FILE* fp = _tfopen(pszFile, _T("rb")); - if (!fp) return 0; - - pIsoCtx->ioType = IO_TYPE_FILE; - pIsoCtx->ioUnion.pFile = fp; - pIsoCtx->bValid = 1; - - pIsoCtx->sectorSize = nSectorLength; - fseek(fp, 0, SEEK_END); - pIsoCtx->totalSectors = (UINT32)(ftell(fp) / nSectorLength); - fseek(fp, 0, SEEK_SET); - return 1; -} - -static void FileIO_Close(struct ISO_CTX* pIsoCtx) -{ - if (!pIsoCtx) return; - if (pIsoCtx->ioType == IO_TYPE_FILE && pIsoCtx->ioUnion.pFile) { - fclose(pIsoCtx->ioUnion.pFile); - pIsoCtx->ioUnion.pFile = NULL; - } - pIsoCtx->bValid = 0; -} - -static void FileIO_Read(struct ISO_CTX* pIsoCtx, UINT8* Dest, UINT32 lOffset, UINT32 lSize, UINT32 lLength) -{ - if (!pIsoCtx || pIsoCtx->ioType != IO_TYPE_FILE) return; - iso9660_ReadOffset(pIsoCtx->ioUnion.pFile, Dest, lOffset, lSize, lLength); -} - -// Read from a CHD via cd_chd. lOffset uses the 2352-stride convention shared -// with the FileIO path (sector*2352 + in-sector byte offset), but the offset -// always lands inside the 2048-byte user area. We translate to a logical LBA -// and read the mode-1 user data, so both 2048 and 2352 source CHDs work. -static void iso9660_ReadOffset_CHD(UINT8* Dest, ChdImage* pChd, UINT32 lOffset, UINT32 lSize, UINT32 lLength) -{ - if (!Dest || !pChd) return; - - UINT32 total_bytes = lSize * lLength; - UINT32 bytes_read = 0; - UINT8 sector[2048]; - - while (bytes_read < total_bytes) { - UINT32 lba = lOffset / nSectorLength; - UINT32 offset_in_user = lOffset % nSectorLength; - if (offset_in_user >= 2048) { - // beyond the user data area (raw sync/ECC region) - pad with zero - memset(Dest + bytes_read, 0, total_bytes - bytes_read); - return; - } - - if (ChdReadSector(pChd, (INT32)lba, CHD_TRACK_MODE1, sector) != 0) { - memset(Dest + bytes_read, 0, total_bytes - bytes_read); - return; - } - - UINT32 avail = 2048 - offset_in_user; - UINT32 to_read = total_bytes - bytes_read; - if (to_read > avail) to_read = avail; - memcpy(Dest + bytes_read, sector + offset_in_user, to_read); - - bytes_read += to_read; - lOffset += to_read; - } -} - -static INT32 ChdIO_Open(struct ISO_CTX* pIsoCtx, TCHAR* pszFile) -{ - if (!pIsoCtx || !pszFile) return 0; - - ChdImage* pChd = ChdOpenFile(pszFile); - if (!pChd) { - bprintf(PRINT_ERROR, _T("ChdIO_Open: ChdOpenFile failed for %s\n"), pszFile); - return 0; - } - - INT32 nContainer = ChdGetContainerType(pChd); - if (nContainer != CHD_CONTAINER_CD && nContainer != CHD_CONTAINER_GDROM) { - ChdClose(pChd); - return 0; - } - - pIsoCtx->ioType = IO_TYPE_CHD; - pIsoCtx->ioUnion.pChd = pChd; - pIsoCtx->bValid = 1; - - // Report user-data sector size and total logical sectors. - pIsoCtx->sectorSize = 2048; - pIsoCtx->totalSectors = (UINT32)ChdGetTotalFrames(pChd); - - return 1; -} - -static void ChdIO_Close(struct ISO_CTX* pIsoCtx) -{ - if (!pIsoCtx || pIsoCtx->ioType != IO_TYPE_CHD) return; - - if (pIsoCtx->ioUnion.pChd) { - ChdClose(pIsoCtx->ioUnion.pChd); - pIsoCtx->ioUnion.pChd = NULL; - } - pIsoCtx->bValid = 0; -} - -static void ChdIO_Read(struct ISO_CTX* pIsoCtx, UINT8* Dest, UINT32 lOffset, UINT32 lSize, UINT32 lLength) -{ - if (!pIsoCtx || pIsoCtx->ioType != IO_TYPE_CHD) return; - iso9660_ReadOffset_CHD(Dest, pIsoCtx->ioUnion.pChd, lOffset, lSize, lLength); -} - -// Match I/O strategy by file extension -static const struct IO_STRATEGY ioStrategyList[] = -{ - { IO_TYPE_FILE, FileIO_Open, FileIO_Close, FileIO_Read }, - { IO_TYPE_CHD, ChdIO_Open, ChdIO_Close, ChdIO_Read } -}; - -static void IsoRead(struct ISO_CTX* pIsoCtx, UINT8* Dest, UINT32 lOffset, UINT32 lSize, UINT32 lLength) -{ - if (!pIsoCtx || !pIsoCtx->bValid) return; - - // Dispatch by I/O type; caller does not need to distinguish between file and CHD - switch (pIsoCtx->ioType) { - case IO_TYPE_FILE: - FileIO_Read(pIsoCtx, Dest, lOffset, lSize, lLength); - break; - case IO_TYPE_CHD: - ChdIO_Read(pIsoCtx, Dest, lOffset, lSize, lLength); - break; - default: - break; - } -} - -static INT32 IsoCheckAndReadLabel(struct ISO_CTX* pIsoCtx) -{ - if (!pIsoCtx || !pIsoCtx->bValid) return 0; - - UINT8 IsoCheck[64] = { 0 }; - // Read CD001 identifier from the PVD area - IsoRead(pIsoCtx, IsoCheck, nSectorLength * 16 + 1, 1, sizeof(IsoCheck)); - - if (!memcmp(IsoCheck, "CD001", 5)) { - // Read and trim the volume label (stored in ISO_CTX) - strncpy(pIsoCtx->szVolumeID, (char*)&IsoCheck[39], 24); - pIsoCtx->szVolumeID[sizeof(pIsoCtx->szVolumeID) - 1] = '\0'; - INT32 l = strlen(pIsoCtx->szVolumeID); - while (l > 0 && pIsoCtx->szVolumeID[l - 1] == ' ') - pIsoCtx->szVolumeID[--l] = '\0'; - -#if DEBUG_CD - bprintf(0, _T("VolumeID is [%S]\n"), pIsoCtx->szVolumeID); -#endif - return 1; - } - bprintf(PRINT_ERROR, _T("ISO9660 CD001 check failed\n")); - return 0; -} - -static UINT32 IsoReadRootSector(struct ISO_CTX* pIsoCtx) -{ - UINT8 buffer[32] = { 0 }; - char szRootSector[32] = { 0 }; - UINT32 nRootSector = 0; - - // Read root directory address at fixed offset 0x9E - IsoRead(pIsoCtx, buffer, nSectorLength * 16 + 0x9e, 1, 8); - snprintf(szRootSector, sizeof(szRootSector), "%02x%02x%02x%02x", buffer[4], buffer[5], buffer[6], buffer[7]); - szRootSector[sizeof(szRootSector) - 1] = '\0'; - sscanf(szRootSector, "%x", &nRootSector); - - return nRootSector; -} - -static void NeoCDList_CheckDirCommon(void (*pfEntryCallBack)(INT32, TCHAR*), TCHAR* pszFile, struct ISO_CTX* pIsoCtx, INT32 nSector) -{ - UINT32 lBytesRead = 0; - UINT32 lOffset = (UINT32)nSector * nSectorLength; - bool bNewSector = false; - bool bRevisionQueve = false; - INT32 nRevisionQueveID = 0; - bool bGotDDPRG_ACM = false; - - TCHAR* pIsoPath = NULL; - if (IsFileExt(pszFile, _T(".cue"))) { - pIsoPath = ParseCueGetImageFile(pszFile); - if (!pIsoPath) return; - - TCHAR szIsoPath[MAX_PATH] = { 0 }; - _tcsncpy(szIsoPath, pIsoPath, MAX_PATH - 1); - szIsoPath[MAX_PATH - 1] = _T('\0'); - free_s((void**)&pIsoPath); - pIsoPath = szIsoPath; - } - - pIsoPath = pszFile; - - UINT8 nLenDR; - UINT8 Flags; - UINT8 LEN_FI; - UINT8* ExtentLoc = (UINT8*)malloc(8 + 64 + 32); - UINT8* Data = (UINT8*)malloc(0x10b + 32); - char* File = (char* )malloc(32 + 32); - - if (!ExtentLoc || !Data || !File) { - free_s((void**)&ExtentLoc); - free_s((void**)&Data); - free_s((void**)&File); - return; - } - - while (1) { - IsoRead(pIsoCtx, &nLenDR, lOffset, 1, sizeof(UINT8)); - - if (nLenDR == 0x22) { - lOffset += nLenDR; - lBytesRead += nLenDR; - continue; - } - if (nLenDR < 0x22) { - if (bNewSector) { - if (bRevisionQueve) { - bRevisionQueve = false; - pfEntryCallBack(nRevisionQueveID, pIsoPath); - } - return; - } - nLenDR = 0; - IsoRead(pIsoCtx, &nLenDR, lOffset + 1, 1, sizeof(UINT8)); - if (nLenDR < 0x22) { - lOffset += (nSectorLength - lBytesRead); - lBytesRead = 0; - bNewSector = true; - continue; - } - } - bNewSector = false; - - IsoRead(pIsoCtx, &Flags, lOffset + 25, 1, sizeof(UINT8)); - - if (!(Flags & (1 << 1))) { - IsoRead(pIsoCtx, ExtentLoc, lOffset + 2, 54, sizeof(UINT8)); - - char szFname[64]; - UINT8 nDate[3] = { 0, 0, 0 }; - memset(szFname, 0, sizeof(szFname)); - INT32 fname_len = (ExtentLoc[30] < 32) ? ExtentLoc[30] : 32; - strncpy(szFname, (char*)&ExtentLoc[31], fname_len); - - if (!strcmp(szFname, "DDPRG.ACM")) bGotDDPRG_ACM = true; - if (fname_len == 0) break; - - nDate[0] = ExtentLoc[16]; - nDate[1] = ExtentLoc[17]; - nDate[2] = ExtentLoc[18]; - -#if 0 - bprintf(0, _T("Checking File [%S] %d-%d-%d\n"), szFname, nDate[0], nDate[1], nDate[2]); -#endif - // Restore original multi-byte composition logic (reverted custom modifications) - UINT32 nValue = - ((UINT32)ExtentLoc[0]) | - ((UINT32)ExtentLoc[1] << 8) | - ((UINT32)ExtentLoc[2] << 16) | - ((UINT32)ExtentLoc[3] << 24); - - IsoRead(pIsoCtx, Data, nValue * nSectorLength, 0x10a, sizeof(UINT8)); - - char szData[32]; - snprintf(szData, sizeof(szData), "%c%c%c%c%c%c%c", Data[0x100], Data[0x101], Data[0x102], Data[0x103], Data[0x104], Data[0x105], Data[0x106]); - szData[sizeof(szData) - 1] = '\0'; - - if (!strncmp(szData, "NEO-GEO", 7)) { - char id[32]; - snprintf(id, sizeof(id), "%02X%02X", Data[0x108], Data[0x109]); - id[sizeof(id) - 1] = '\0'; - UINT32 nID = 0; - sscanf(id, "%x", &nID); -#if 0 - bprintf(0, _T("----- ID: %x\n"), nID); -#endif - IsoRead(pIsoCtx, &LEN_FI, lOffset + 32, 1, sizeof(UINT8)); - IsoRead(pIsoCtx, (UINT8*)File, lOffset + 33, LEN_FI, sizeof(UINT8)); - File[LEN_FI] = 0; - - if (nID == 0x0016 && nDate[0] == 94 && nDate[1] == 12 && nDate[2] == 20) { nID |= 0x1000; } // Justin Gibbons Hacks (kotm) - if (nID == 0x0025 && nDate[0] == 94 && nDate[1] == 12 && nDate[2] == 20) { nID |= 0x1000; } // Justin Gibbons Hacks (eightman) - if (nID == 0x0076 && nDate[0] == 94 && nDate[1] == 12 && nDate[2] == 20) { nID |= 0x1000; } // Justin Gibbons Hacks (zedblade) - if (nID == 0x0062 && nDate[0] == 94 && nDate[1] == 12 && nDate[2] == 20) { nID |= 0x1000; } // Justin Gibbons Hacks (spinmast) - if (nID == 0x0059 && nDate[0] == 95 && nDate[1] == 6 && nDate[2] == 20) { nID |= 0x1000; } // Savage Reign Rev 1 - if (nID == 0x0066 && nDate[0] == 125 && nDate[1] == 4 && nDate[2] == 10) { nID |= 0x1200; } // Digger Man (Prototype) - if (nID == 0x069c && nDate[0] == 95 && nDate[1] == 4 && nDate[2] == 29) { nID |= 0x1000; } // Fatal Fury 3 Rev 1 - if (nID == 0x069c && nDate[0] == 95 && nDate[1] == 6 && nDate[2] == 1) { nID |= 0x2000; } // Fatal Fury 3 Rev 2 - if (nID == 0x069c && nDate[0] == 95 && nDate[1] == 7 && nDate[2] == 10) { nID |= 0x3000; } // Fatal Fury 3 Rev 3 - if (nID == 0x0090 && nDate[0] == 95 && nDate[1] == 7 && nDate[2] == 21) { nID |= 0x1000; } // World Heroes Perfect - if (nID == 0x0058 && nDate[0] == 94 && nDate[1] == 10 && nDate[2] == 14) { nID |= 0x1000; } // Fatal Fury Special Rev 1 - if (nID == 0x0052 && nDate[0] == 123 && nDate[1] == 7 && nDate[2] == 1) { nID |= 0x1000; } // Abyssal Infants - if (nID == 0x0052 && nDate[0] == 123 && nDate[1] == 6 && nDate[2] == 20) { nID |= 0x1001; } // Neo Fight - if (nID == 0x0082) { - if (bGotDDPRG_ACM) { nID |= 0x1000; } // Double Dragon Rev 1 - else if (StrStrI(pIsoPath, _T("OST")) || - StrStrI(pIsoPath, _T("PS")) || - StrStrI(pIsoPath, _T("PlayStation"))) { nID |= 0x2000; } // Double Dragon PS1 OST - } - if (nID == 0x0085) { - if (nDate[0] == 123 && nDate[1] == 11 && nDate[2] == 29) { nID |= 0x1000; } // Samurai Shodown RPG (English Translation) - else if (nDate[0] == 124 && nDate[1] == 1 && nDate[2] == 26) { nID |= 0x3000; } // Samurai Shodown RPG (English Translation v1.1) - else if (nDate[0] == 126 && nDate[1] == 6 && nDate[2] == 1) { nID |= 0x4000; } // Samurai Shodown RPG (Simplified Chinese Translation v1.1) - else if (StrStrI(pIsoPath, _T("FR")) || - StrStrI(pIsoPath, _T("French"))) { nID |= 0x2000; } // Samurai Shodown RPG (FR) - } - if (nID == 0x5345) { - if (!strcmp(pIsoCtx->szVolumeID, "BLUEANDREDFIGHTTHEROBOTS") || - (StrStrI(pIsoPath, _T("NTSC")))) { nID = 0x5346; } // Blue And Red - Fight The Robots! (NTSC) - } - if (nID == 0x1234 && nDate[0] == 105 && nDate[1] == 4 && nDate[2] == 25) { nID = 0x2234; } // Neo Puzzle League - if (nID == 0x1234 && nDate[0] == 124 && nDate[1] == 12 && nDate[2] == 2) { nID = 0x2235; } // Neo Tetris - if (nID == 0x1234 && nDate[0] == 112 && nDate[1] == 3 && nDate[2] == 4) { nID = 0x2236; } // NGD::ARK - if (nID == 0x1234 && nDate[0] == 112 && nDate[1] == 12 && nDate[2] == 4) { nID = 0x2237; } // Santa Ball - if (nID == 0x2000 && !strcmp(pIsoCtx->szVolumeID, "COLUMNS")) { nID |= 0x1000; } // Columns - if (nID == 0xFFFF && !strcmp(pIsoCtx->szVolumeID, "CODENAME BLUT ENGEL")) { nID = 0xFFFE; } // Codename Blut Engel - if (nID == 0x7777 && nDate[0] == 114 && nDate[1] == 8 && nDate[2] == 14) { nID = 0x7778; } // Puzzle de Pon! CD Collection - if (nID == 0x2019 && !strcmp(pIsoCtx->szVolumeID, "LOOPTRSP")) { nID |= 0x0100; } // Looptris Plus - if (nID == 0x0048 && Data[0x67] == 0x08) { nID |= 0x1000; } // Treasure of Caribbean (c) 1994 / (c) 2011 NCI - if (nID == 0x0055 && Data[0x67] == 0xDE) /* 10-6-1994 (P1.PRG) */ {/* ...continue*/} // King of Fighters '94, The (1994)(SNK)(JP) - if (nID == 0x0055 && Data[0x67] == 0xE6) /* 11-21-1994 (P1.PRG) */ { nID |= 0x1000; } // King of Fighters '94, The (1994)(SNK)(JP-US) - if (nID == 0x0084 && Data[0x6C] == 0xC0) /* 9-11-1995 (P1.PRG) */ {/* ...continue*/} // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B01, B03-B06, NGCD-084E MT B01] - if (nID == 0x0084 && Data[0x6C] == 0xFF) /* 10-5-1995 (P1.PRG) */ { nID |= 0x1000; } // King of Fighters '95, The (1995)(SNK)(JP-US)[!][NGCD-084 MT B10, NGCD-084E MT B03] - if (nID == 0x0229) { // King of Fighters '96 NEOGEO Collection, The - bRevisionQueve = false; - pfEntryCallBack(nID, pszFile); - break; - } - // Sometimes there's multiple .prg entries in the - // list, .old / .bak files which preceed the proper one.. - // For these, we have to keep searching the file list: - if (nID == 0x0214 // King of Fighters '96, The - || (nID == 0x0058 && !(nDate[0] == 94 && nDate[1] == 8 && nDate[2] == 5))) { // !Fatal Fury Special Rev 0 - // continue checking other files... - bRevisionQueve = true; - nRevisionQueveID = nID; - lOffset += nLenDR; - lBytesRead += nLenDR; - continue; - } - pfEntryCallBack(nID, pszFile); - break; - } - } - lOffset += nLenDR; - lBytesRead += nLenDR; - } - - // Unified safe release - free_s((void**)&ExtentLoc); - free_s((void**)&Data); - free_s((void**)&File); -} - -static void PrintNGCDGameInfo(const NGCDGAME* pGame) -{ - if (!pGame) return; - bprintf(PRINT_NORMAL, _T(" Title: %s \n"), pGame->pszTitle); - bprintf(PRINT_NORMAL, _T(" Shortname: %s \n"), pGame->pszName); - bprintf(PRINT_NORMAL, _T(" Year: %s \n"), pGame->pszYear); - bprintf(PRINT_NORMAL, _T(" Company: %s \n"), pGame->pszCompany); -} - -// Internal reusable tool: deep copy game meta by ID, output via ppOutGame -// Return 1 success, 0 fail -INT32 GetNGCDGameTitle(const UINT32 nGameID, NGCDGAME** ppOutGame, bool bPrintLog) -{ - if (!ppOutGame) - return 0; - - *ppOutGame = NULL; - NGCDGAME* pGameInfo = GetNeoGeoCDInfo(nGameID); - if (!pGameInfo) { - bprintf(0, _T("NeoGeoCD Unknown GAME ID %x\n"), nGameID); - return 0; - } - - NGCDGAME* pnew = (NGCDGAME*)calloc(1, sizeof(NGCDGAME)); - if (!pnew) - return 0; - - // Deep copy all fields - pnew->id = pGameInfo->id; - pnew->pszName = _tcsdup_s(pGameInfo->pszName); - if (!pnew->pszName) - goto CLEAN_FAIL; - - pnew->pszTitle = _tcsdup_s(pGameInfo->pszTitle); - if (!pnew->pszTitle) - goto CLEAN_FAIL; - - pnew->pszYear = _tcsdup_s(pGameInfo->pszYear); - if (!pnew->pszYear) - goto CLEAN_FAIL; - - pnew->pszCompany = _tcsdup_s(pGameInfo->pszCompany); - if (!pnew->pszCompany) - goto CLEAN_FAIL; - - // Print only when switch enabled - if (bPrintLog) - PrintNGCDGameInfo(pnew); - - *ppOutGame = pnew; - return 1; - -CLEAN_FAIL: - free_s((void**)&pnew->pszName); - free_s((void**)&pnew->pszTitle); - free_s((void**)&pnew->pszYear); - free_s((void**)&pnew->pszCompany); - free_s((void**)&pnew); - return 0; -} - -INT32 NeoCDList_CheckISO(TCHAR* pszFile, void (*pfEntryCallBack)(INT32, TCHAR*)) -{ - if (!pszFile) - return 0; - - TCHAR* pIsoPath = NULL; - if (IsFileExt(pszFile, _T(".cue"))) { - pIsoPath = ParseCueGetImageFile(pszFile); - if (!pIsoPath) return 0; - - TCHAR szIsoPath[MAX_PATH] = { 0 }; - _tcsncpy(szIsoPath, pIsoPath, MAX_PATH - 1); - szIsoPath[MAX_PATH - 1] = _T('\0'); - free_s((void**)&pIsoPath); - pIsoPath = szIsoPath; - } - - if (!pIsoPath) pIsoPath = pszFile; - - INT32 ret = 0; - // Match I/O strategy - const struct IO_STRATEGY* pStrategy = NULL; - if ( IsFileExt(pIsoPath, _T(".img")) || - IsFileExt(pIsoPath, _T(".bin"))) { - pStrategy = &ioStrategyList[0]; - } else if (IsFileExt(pIsoPath, _T(".chd"))) { - pStrategy = &ioStrategyList[1]; - } else { - // Unsupported format, exit directly - return 0; - } - - // Allocate top-level context - struct ISO_CTX* pIsoCtx = (struct ISO_CTX*)calloc(1, sizeof(struct ISO_CTX)); - if (!pIsoCtx) return 0; - - // Open image and initialize context - if (!pStrategy->fnOpen(pIsoCtx, pIsoPath)) { - free_s((void**)&pIsoCtx); - return 0; - } - - // Minimum sector count check - if (pIsoCtx->totalSectors <= 16) { - pStrategy->fnClose(pIsoCtx); - free_s((void**)&pIsoCtx); - return 0; - } - - // ISO identifier and volume label check - if (IsoCheckAndReadLabel(pIsoCtx)) { - UINT32 nRootSector = IsoReadRootSector(pIsoCtx); - // Call the single unified directory function - NeoCDList_CheckDirCommon(pfEntryCallBack, pszFile, pIsoCtx, (INT32)nRootSector); - ret = 1; - } - - // Unified resource cleanup and context release - pStrategy->fnClose(pIsoCtx); - free_s((void**)&pIsoCtx); - return ret; -} - -NGCDGAME* GetNeoGeoCDInfo(UINT32 nID) -{ - for(UINT32 nGame = 0; nGame < (sizeof(games) / sizeof(NGCDGAME)); nGame++) { - if(nID == games[nGame].id ) { - return &games[nGame]; - } - } - - return NULL; -} - -// Update the main window title -static void SetNeoCDTitle(TCHAR* pszTitle) -{ - TCHAR szText[1024] = _T(""); - _sntprintf(szText, ARRAY_SIZE(szText), _T(APP_TITLE) _T( " v%.20s") _T(SEPERATOR_1) _T("%s") _T(SEPERATOR_1) _T("%s"), szAppBurnVer, BurnDrvGetText(DRV_FULLNAME), pszTitle); - szText[ARRAY_SIZE(szText) - 1] = '\0'; - SetWindowText(hScrnWnd, szText); -} - -void NeoCDInfo_SetTitle() -{ - if (IsNeoGeoCD() && game && game->id) { - SetNeoCDTitle(game->pszTitle); - } else if (IsNeoGeoCD()) { - SetNeoCDTitle(FBALoadStringEx(hAppInst, IDS_UNIDENTIFIED_CD, true)); - } -} - -void FreeNGCDGame(NGCDGAME** ppGame) -{ - if (!ppGame || !*ppGame) - return; - - NGCDGAME* p = *ppGame; - free_s((void**)&p->pszName); - free_s((void**)&p->pszTitle); - free_s((void**)&p->pszYear); - free_s((void**)&p->pszCompany); - free_s((void**)ppGame); -} - -// Get the title -INT32 GetNeoCDTitle(UINT32 nGameID) -{ - // Release previous global data first to avoid memory leak - FreeNGCDGame(&game); - // Global scene needs log output, pass true - return GetNGCDGameTitle(nGameID, &game, true); -} - -static void NeoCDList_Callback(INT32 nID, TCHAR *pszFile) -{ - GetNeoCDTitle(nID); -} - -void NeoCDInfo_Exit() -{ - FreeNGCDGame(&game); -} - -// This function scans and identifies NeoGeo CD game information from supported image files -INT32 GetNeoGeoCD_Identifier() -{ - TCHAR* szIsoPath = GetIsoPath(); - // Skip if ISO path is empty or NeoGeo CD mode is disabled - if (!szIsoPath || !IsNeoGeoCD()) - return 0; - - // Clear previous game global data to avoid old info pollution - NeoCDInfo_Exit(); - - // Check supported image extensions: img / bin / chd - if (IsFileExt(szIsoPath, _T(".img")) || IsFileExt(szIsoPath, _T(".bin")) || IsFileExt(szIsoPath, _T(".chd"))) { - bprintf(0, _T("NeoCDList: checking %s\n"), szIsoPath); - INT32 ret = NeoCDList_CheckISO(szIsoPath, NeoCDList_Callback); - if (!ret) { - bprintf(PRINT_NORMAL, _T(" Failed to parse image file %s\n"), szIsoPath); - return 0; - } - return 1; - } else { - bprintf(PRINT_NORMAL, _T(" File doesn't have a valid ISO extension [.img / .bin / .chd]\n")); - return 0; - } -} - -INT32 NeoCDInfo_Init() -{ - NeoCDInfo_Exit(); - return GetNeoGeoCD_Identifier(); -} - -TCHAR* NeoCDInfo_Text(INT32 nText) -{ - if(!game || !IsNeoGeoCD()) return NULL; - - switch(nText) { - case DRV_NAME: return game->pszName; - case DRV_FULLNAME: return game->pszTitle; - case DRV_MANUFACTURER: return game->pszCompany; - case DRV_DATE: return game->pszYear; - } - - return NULL; -} - -INT32 NeoCDInfo_ID() -{ - if(!game || !IsNeoGeoCD()) return 0; - return game->id; -} - -#endif \ No newline at end of file diff --git a/src/burner/win32/neocdsel.cpp b/src/burner/win32/neocdsel.cpp index 727d62128..ad7e95bbe 100644 --- a/src/burner/win32/neocdsel.cpp +++ b/src/burner/win32/neocdsel.cpp @@ -40,17 +40,17 @@ const INT32 nSectorLength = 2352; // Stores single NeoGeo CD game metadata struct GAMELIST { - bool bFoundCUE; // True if game loaded from .cue sheet file - TCHAR* szPathCUE; // Full absolute path of cue file - TCHAR* szPath; // Full absolute path of real image file (chd/bin) - TCHAR* szISOFile; // Raw image filename extracted from cue - TCHAR* szGameId; // Game identification number string - TCHAR* szShortName; // Game short name for cover matching - TCHAR* szTitle; // Game title - TCHAR* szPublisher; // Publisher + release year combined string - TCHAR* szAudioTracks; // Audio tracks string -// TCHAR szTracks[99][256]; // Unused track storage array, commented out - INT32 nIconIdx; // index inside NeoCD_ImageList + INT32 nPlatform; + UINT64 nKey; + TCHAR* szPath; + TCHAR* szISOFile; + TCHAR* szGameId; + TCHAR* szShortName; + TCHAR* szTitle; + TCHAR* szPublisher; + TCHAR* szAudioTracks; + TCHAR* szRequirement; + INT32 nIconIdx; }; // Central game library container, thread-safe with critical section lock @@ -389,13 +389,13 @@ static void FreeGameItem(struct GAMELIST* pItem) if (!pItem) return; - free_s((void**)&pItem->szPathCUE); free_s((void**)&pItem->szPath); free_s((void**)&pItem->szISOFile); free_s((void**)&pItem->szShortName); free_s((void**)&pItem->szPublisher); free_s((void**)&pItem->szTitle); free_s((void**)&pItem->szAudioTracks); + free_s((void**)&pItem->szRequirement); free_s((void**)&pItem->szGameId); } @@ -435,11 +435,9 @@ static BOOL GameLib_AddGame(struct GAME_LIB* pLib, struct GAMELIST* pSrcTemplate struct GAMELIST* pDst = &pNew[pLib->dataCount]; // Copy numeric value fields from source template - pDst->bFoundCUE = pSrcTemplate->bFoundCUE; + pDst->nPlatform = pSrcTemplate->nPlatform; + pDst->nKey = pSrcTemplate->nKey; pDst->nIconIdx = pSrcTemplate->nIconIdx; - - // Transfer string pointer ownership (share original heap memory, no duplication) - pDst->szPathCUE = pSrcTemplate->szPathCUE; pDst->szPath = pSrcTemplate->szPath; pDst->szISOFile = pSrcTemplate->szISOFile; pDst->szShortName = pSrcTemplate->szShortName; @@ -447,6 +445,7 @@ static BOOL GameLib_AddGame(struct GAME_LIB* pLib, struct GAMELIST* pSrcTemplate pDst->szTitle = pSrcTemplate->szTitle; pDst->szGameId = pSrcTemplate->szGameId; pDst->szAudioTracks = pSrcTemplate->szAudioTracks; + pDst->szRequirement = pSrcTemplate->szRequirement; // Replace old game data array with reallocated buffer and update item counter pLib->pGameData = pNew; @@ -511,313 +510,57 @@ static void GameLib_Destroy(struct GAME_LIB* pLib) pGameLib = NULL; } -// ParseCueGetImageFile -// Return NULL if file open failed / no valid FILE BINARY segment found -TCHAR* ParseCueGetImageFile(const TCHAR* cueFullPath) +static struct GAMELIST* NeoCD_BuildGameEntry(const TCHAR* pszPath, const CDListResult* pResult) { - if (IsStrEmpty(cueFullPath)) - return NULL; + if (!pszPath || !pResult || pResult->nPlatform != CDLIST_PLATFORM_NEOCD) return NULL; + CDImage* pImage = CDImageOpen(pszPath); + if (!pImage) return NULL; - FILE* fp = _tfopen(cueFullPath, _T("r")); - if (!fp) - return NULL; - - TCHAR szCueDir[2048] = { 0 }; - size_t nBufSize = ARRAY_SIZE(szCueDir); - _tcsncpy(szCueDir, cueFullPath, nBufSize - 1); - szCueDir[nBufSize - 1] = _T('\0'); - - TCHAR* pSep = _tcsrchr(szCueDir, _T('\\')); - if (pSep) - *(pSep + 1) = _T('\0'); - - bool bFoundBinary = false; - TCHAR szImgName[2048] = { 0 }; - TCHAR szBuffer[ 2048] = { 0 }; - - while (_fgetts(szBuffer, ARRAY_SIZE(szBuffer), fp)) { - // Trim trailing whitespace & control characters (\r \n space etc.) - INT32 nLen = _tcslen(szBuffer); - while (nLen > 0 && szBuffer[nLen - 1] < 32) - { - szBuffer[nLen - 1] = _T('\0'); - nLen--; - } - - // Match FILE "filename" BINARY line - if (!_tcsncmp(szBuffer, _T("FILE"), 4)) { - TCHAR* pEndQ = _tcsrchr(szBuffer, _T('"')); - if (!pEndQ) - continue; - *pEndQ = _T('\0'); - - TCHAR* pStartQ = _tcschr(szBuffer, _T('"')); - if (!pStartQ) - continue; - // Skip if segment is not BINARY type - if (_tcsstr(pEndQ + 1, _T("BINARY")) == NULL) - continue; - - // Extract image file name inside quotes - nBufSize = ARRAY_SIZE(szImgName); - _tcsncpy(szImgName, pStartQ + 1, nBufSize - 1); - szImgName[nBufSize - 1] = _T('\0'); - bFoundBinary = true; + const CDImageTrack* pDataTrack = NULL; + for (INT32 i = 0; i < CDImageGetTrackCount(pImage); i++) { + const CDImageTrack* pTrack = CDImageGetTrack(pImage, i); + if (pTrack && pTrack->nType != CDIMAGE_TRACK_AUDIO) { + pDataTrack = pTrack; break; } } - fclose(fp); - if (!bFoundBinary) - return NULL; - - // Combine cue directory and image filename to full absolute path - TCHAR* pFullImagePath = NULL; - PathCombine_s(&pFullImagePath, szCueDir, szImgName); - return pFullImagePath; -} - -// Parse cue sheet file, fill path info & audio track count -// Return heap-allocated GAMELIST entry on success, NULL if failed -static struct GAMELIST* ParseCueCreateGameItem(const TCHAR* cueFullPath) -{ - // Params validated upstream; skip duplicate check here - // Allocate zero-init struct, all pointers default NULL struct GAMELIST* pItem = (struct GAMELIST*)calloc(1, sizeof(struct GAMELIST)); - if (!pItem) - return NULL; - - pItem->nIconIdx = 0; - - FILE* fp = _tfopen(cueFullPath, _T("r")); - if (!fp) { - free_s((void**)&pItem); + if (!pItem) { + CDImageClose(pImage); return NULL; } - // Extract directory folder from full cue path, use safe _tcsncpy - TCHAR szCueDir[2048] = { 0 }; - size_t nStrLen = ARRAY_SIZE(szCueDir); - _tcsncpy(szCueDir, cueFullPath, nStrLen - 1); - szCueDir[nStrLen - 1] = _T('\0'); + pItem->nPlatform = pResult->nPlatform; + pItem->nKey = pResult->nNeoID; + pItem->nIconIdx = IsFileExt((TCHAR*)pszPath, _T(".chd")) ? 1 : 0; + pItem->szPath = _tcsdup_s(CDImageGetPath(pImage)); + pItem->szISOFile = _tcsdup_s(pDataTrack ? pDataTrack->szPath : CDImageGetPath(pImage)); + pItem->szShortName = _tcsdup_s(pResult->Metadata.szName); + pItem->szTitle = _tcsdup_s(pResult->Metadata.szTitle); + pItem->szRequirement = _tcsdup_s(pResult->Metadata.szRequirement); - TCHAR* pSep = _tcsrchr(szCueDir, _T('\\')); - if (pSep) - *(pSep + 1) = _T('\0'); + TCHAR szText[256] = { 0 }; + _sntprintf(szText, ARRAY_SIZE(szText), _T("%s (%s)"), pResult->Metadata.szCompany, pResult->Metadata.szYear); + szText[ARRAY_SIZE(szText) - 1] = _T('\0'); + pItem->szPublisher = _tcsdup_s(szText); + _sntprintf(szText, ARRAY_SIZE(szText), _T("%04X"), pResult->nNeoID); + szText[ARRAY_SIZE(szText) - 1] = _T('\0'); + pItem->szGameId = _tcsdup_s(szText); + _sntprintf(szText, ARRAY_SIZE(szText), _T("%d"), CDImageGetAudioTrackCount(pImage)); + szText[ARRAY_SIZE(szText) - 1] = _T('\0'); + pItem->szAudioTracks = _tcsdup_s(szText); + CDImageClose(pImage); - bool bHasBinary = false; - TCHAR szBuffer[2048] = { 0 }; - const size_t nBufSize = ARRAY_SIZE(szBuffer); - - INT32 nAudioTracks = 0; - while (_fgetts(szBuffer, (INT32)nBufSize, fp)) { - // Trim trailing ASCII control chars (\r \n etc.) - INT32 nLen = _tcslen(szBuffer); - while (nLen > 0 && szBuffer[nLen - 1] < 32) { - szBuffer[nLen - 1] = _T('\0'); - nLen--; - } - - // Parse FILE "filename" BINARY segment - if (!_tcsncmp(szBuffer, _T("FILE"), 4)) { - TCHAR* pEndQ = _tcsrchr(szBuffer, _T('"')); - if (!pEndQ) - continue; - *pEndQ = _T('\0'); - - TCHAR* pStartQ = _tcschr(szBuffer, _T('"')); - if (!pStartQ) - continue; - if (!_tcsstr(pEndQ + 1, _T("BINARY"))) - continue; - - pItem->szISOFile = _tcsdup_s(pStartQ + 1); - pItem->szPathCUE = _tcsdup_s(cueFullPath); - - TCHAR szFullImg[2048] = { 0 }; - nStrLen = ARRAY_SIZE(szFullImg); - _sntprintf(szFullImg, nStrLen, _T("%s%s"), szCueDir, pStartQ + 1); - szFullImg[nStrLen - 1] = _T('\0'); - pItem->szPath = _tcsdup_s(szFullImg); - - pItem->bFoundCUE = true; - bHasBinary = true; - } - - // Parse TRACK definition, same pointer logic as original source - TCHAR* t = LabelCheck(szBuffer, _T("TRACK")); - if (t) { - TCHAR* s = t; - // Parse track numeric id, advance t past digits - _tcstol(s, &t, 10); - s = t; - - // Skip leading spaces to reach the type field - while (*s == _T(' ')) s++; - - // CD-DA audio track, increment counter - if (!_tcsncmp(s, _T("AUDIO"), 5)) { - nAudioTracks++; - continue; - } - // Any MODE* type (MODE1/2048, MODE1/2352, MODE2/xxxx ...) is a data track - if (!_tcsncmp(s, _T("MODE"), 4)) - continue; - } - } - fclose(fp); - - memset(szBuffer, 0, nBufSize); - _sntprintf(szBuffer, nBufSize, _T("%d"), nAudioTracks); - szBuffer[nBufSize - 1] = _T('\0'); - pItem->szAudioTracks = _tcsdup_s(szBuffer); - - // No valid binary found in cue sheet, release resource - if (!bHasBinary) { + if (!pItem->szPath || !pItem->szISOFile || !pItem->szShortName || !pItem->szTitle || + !pItem->szPublisher || !pItem->szAudioTracks || !pItem->szGameId) { FreeGameItem(pItem); free_s((void**)&pItem); return NULL; } - return pItem; } -// Create base GAMELIST entry for CHD file, fill path & audio track count from CHD header -// Param chdFullPath: full absolute path of target chd file -// Return heap-allocated GAMELIST on success, NULL on failure -static struct GAMELIST* CreateChdBaseGameItem(TCHAR* chdFullPath) -{ - struct GAMELIST* pItem = (struct GAMELIST*)calloc(1, sizeof(struct GAMELIST)); - if (!pItem) - return NULL; - - pItem->nIconIdx = 1; - - // Store full CHD absolute path - pItem->szPath = _tcsdup_s(chdFullPath); - if (!pItem->szPath) { - free_s((void**)&pItem); - return NULL; - } - - // Extract short file name for szISOFile - TCHAR szFileName[2048] = { 0 }; - size_t nLen = ARRAY_SIZE(szFileName) - 1; - _tcsncpy(szFileName, chdFullPath, nLen); - szFileName[nLen] = _T('\0'); - - TCHAR* pNameStart = _tcsrchr(szFileName, _T('\\')); - if (pNameStart) - pNameStart++; - else - pNameStart = szFileName; - - pItem->szISOFile = _tcsdup_s(pNameStart); - if (!pItem->szISOFile) { - FreeGameItem(pItem); - free_s((void**)&pItem); - return NULL; - } - - pItem->bFoundCUE = FALSE; - - // Get audio track count from CHD metadata - const INT32 nAudioTracks = cdimgCountChdAudioTracks(chdFullPath); - - TCHAR szBuffer[10] = { 0 }; - size_t nSize = ARRAY_SIZE(szBuffer); - _sntprintf(szBuffer, nSize, _T("%d"), nAudioTracks); - szBuffer[nSize - 1] = _T('\0'); - pItem->szAudioTracks = _tcsdup_s(szBuffer); - - return pItem; -} - -// Unified entry: build game item for both .cue and .chd file -// Return TRUE if entry created successfully, FALSE on any failure / unsupported extension -static BOOL NeoCD_BuildGameEntry(UINT32 nGameID, TCHAR* filePath, struct GAMELIST** ppOutItem) -{ - if (IsStrEmpty(filePath) || !ppOutItem) - return FALSE; - - *ppOutItem = NULL; - INT32 nImgIdx = -1; - - struct GAMELIST* pItem = NULL; - if ( IsFileExt(filePath, _T(".cue"))) { - pItem = ParseCueCreateGameItem(filePath); - nImgIdx = 0; - } else if (IsFileExt(filePath, _T(".chd"))) { - pItem = CreateChdBaseGameItem( filePath); - nImgIdx = 1; - } else { - // Unsupported format - return FALSE; - } - - if (!pItem) - return FALSE; - - NGCDGAME* pMeta = NULL; - if (!GetNGCDGameTitle(nGameID, &pMeta)) { - FreeGameItem(pItem); - free_s((void**)&pItem); - return FALSE; - } - - // Combine publisher & release year string - TCHAR szBuffer[256] = { 0 }; - const size_t nLen = ARRAY_SIZE(szBuffer); - _sntprintf(szBuffer, nLen, _T("%s (%s)"), pMeta->pszCompany, pMeta->pszYear); - szBuffer[nLen - 1] = _T('\0'); - pItem->szPublisher = _tcsdup_s(szBuffer); - if (!pItem->szPublisher) { - FreeNGCDGame(&pMeta); - FreeGameItem(pItem); - free_s((void**)&pItem); - return FALSE; - } - - // Fill game identity & display text fields - pItem->szShortName = _tcsdup_s(pMeta->pszName); - if (!pItem->szShortName) { - FreeNGCDGame(&pMeta); - FreeGameItem(pItem); - free_s((void**)&pItem); - return FALSE; - } - - if (nGameID == 0x0000) - pItem->szTitle = _tcsdup_s(pItem->szPath); - else - pItem->szTitle = _tcsdup_s(pMeta->pszTitle); - if (!pItem->szTitle) { - FreeNGCDGame(&pMeta); - FreeGameItem(pItem); - free_s((void**)&pItem); - return FALSE; - } - - memset(szBuffer, 0, sizeof(szBuffer)); - _sntprintf(szBuffer, nLen, _T("%04X"), nGameID); - szBuffer[nLen - 1] = _T('\0'); - pItem->szGameId = _tcsdup_s(szBuffer); - if (!pItem->szGameId) { - FreeNGCDGame(&pMeta); - FreeGameItem(pItem); - free_s((void**)&pItem); - return FALSE; - } - - // Assign icon index based on file type - pItem->nIconIdx = nImgIdx; - - FreeNGCDGame(&pMeta); - - *ppOutItem = pItem; - return TRUE; -} - static bool NeoCD_GameLibInit() { // Lazy init game lib @@ -831,57 +574,27 @@ static bool NeoCD_GameLibInit() return true; } -// Callback function for directory scan, construct and store game metadata into game library -static void NeoCD_AddGameLib_Callback(INT32 nGameID, TCHAR* filePath) +static INT32 NeoCD_AddGameLib(const TCHAR* pszPath) { - struct GAMELIST* pTempItem = NULL; - // Build temporary game metadata entry from cue/chd file - if (!NeoCD_BuildGameEntry((UINT32)nGameID, filePath, &pTempItem) || !pTempItem) { - bprintf(PRINT_ERROR, _T("NeoCD_AddGameLib_Callback: build entry fail ID=0x%04X %s\n"), nGameID, filePath); - return; - } - - // Transfer string pointer ownership to global game library array - BOOL bOk = GameLib_AddGame(pGameLib, pTempItem); - if (!bOk) { - bprintf(PRINT_ERROR, _T("NeoCD_AddGameLib_Callback: add to lib memory fail\n")); - } - - // Only release temporary GAMELIST struct shell, do NOT free internal string buffers - // Strings are now owned by GAME_LIB and will be released in GameLib_Destroy - free_s((void**)&pTempItem); -} - -// Add cue/chd game to static global game lib -static INT32 NeoCD_AddGameLib(const TCHAR* filePath) -{ - // Check user cancel signal before processing current file - if (pGameLib && pGameLib->hGLEvent) { - // Non-blocking wait for abort event - if (WaitForSingleObject(pGameLib->hGLEvent, 0) == WAIT_OBJECT_0) { - // Return negative value to tell directory traversal to stop scan - return -1; - } - } - - if (IsStrEmpty(filePath)) { - bprintf(PRINT_ERROR, _T("NeoCD_AddGameLib: empty or invalid path\n")); - return 0; - } - - if (!IsFileExt((TCHAR*)filePath, _T(".cue")) && !IsFileExt((TCHAR*)filePath, _T(".chd"))) - return 0; + if (pGameLib && pGameLib->hGLEvent && WaitForSingleObject(pGameLib->hGLEvent, 0) == WAIT_OBJECT_0) return -1; + if (!pszPath || (!IsFileExt((TCHAR*)pszPath, _T(".cue")) && !IsFileExt((TCHAR*)pszPath, _T(".ccd")) && !IsFileExt((TCHAR*)pszPath, _T(".chd")))) return 0; SendDlgItemMessage(pGameLib->hGLDlg, IDC_WAIT_PROG, PBM_STEPIT, 0, 0); + CDListResult Result; + if (!CDListIdentify(pszPath, &Result) || Result.nPlatform != CDLIST_PLATFORM_NEOCD) return 1; - NeoCDList_CheckISO((TCHAR*)filePath, NeoCD_AddGameLib_Callback); - + struct GAMELIST* pItem = NeoCD_BuildGameEntry(pszPath, &Result); + if (!pItem) return 1; + if (!GameLib_AddGame(pGameLib, pItem)) { + FreeGameItem(pItem); + } + free_s((void**)&pItem); return 1; } static INT32 NeoCD_GetCDImageCount(const TCHAR* filePath) { - if (!IsFileExt((TCHAR*)filePath, _T(".cue")) && !IsFileExt((TCHAR*)filePath, _T(".chd"))) + if (!IsFileExt((TCHAR*)filePath, _T(".cue")) && !IsFileExt((TCHAR*)filePath, _T(".ccd")) && !IsFileExt((TCHAR*)filePath, _T(".chd"))) return 0; pGameLib->progressCount++; @@ -1442,104 +1155,6 @@ static bool FileExists(TCHAR *tcszFile) DWORD dwAttrib = GetFileAttributes(tcszFile); return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } - -#if 0 -// This will parse the specified CUE file and return the ISO path, if found -static TCHAR* NeoCDList_ParseCUE(TCHAR* pszFile) -{ - //if(!pszFile) return NULL; - - TCHAR* szISO = NULL; - szISO = (TCHAR*)malloc(sizeof(TCHAR) * 2048); - if(!szISO) return NULL; - - // open file - FILE* fp = NULL; - fp = _tfopen(pszFile, _T("r")); - - if(!fp) { - if (szISO) - { - free(szISO); - return NULL; - } - } - - while(!feof(fp)) - { - TCHAR szBuffer[2048]; - TCHAR szOriginal[2048]; - TCHAR* s; - TCHAR* t; - - _fgetts(szBuffer, 2048, fp); - - INT32 nLength = 0; - nLength = _tcslen(szBuffer); - - // Remove ASCII control characters from the string (including the 'space' character) - while (szBuffer[nLength-1] < 32 && nLength > 0) - { - szBuffer[nLength-1] = 0; - nLength--; - } - - _tcscpy(szOriginal, szBuffer); - - if(!_tcsncmp(szBuffer, _T("FILE"), 4)) - { - TCHAR* pEnd = _tcsrchr(szBuffer, '"'); - if (!pEnd) { - break; // Invalid CUE format - } - - *pEnd = 0; - - TCHAR* pStart = _tcschr(szBuffer, '"'); - - if(!pStart) { - break; // Invalid CUE format - } - - if(!_tcsncmp(pEnd + 2, _T("BINARY"), 6)) - { - _tcscpy(szISO, pStart + 1); - ngcd_list[nListItems].bFoundCUE = true; - _tcscpy(ngcd_list[nListItems].szPathCUE, pszFile); - _tcscpy(ngcd_list[nListItems].szISOFile, pStart + 1); - } - } - // track info - if ((t = LabelCheck(szBuffer, _T("TRACK"))) != 0) { - s = t; - - // track number - /*UINT8 track = */ _tcstol(s, &t, 10); - - s = t; - - // type of track - - if ((t = LabelCheck(s, _T("MODE1/2352"))) != 0) { - //bprintf(0, _T(".cue: Track #%d, data.\n"), track); - continue; - } - if ((t = LabelCheck(s, _T("AUDIO"))) != 0) { - //bprintf(0, _T(".cue: Track #%d, AUDIO.\n"), track); - ngcd_list[nListItems].nAudioTracks++; - continue; - } - - fclose(fp); - return szISO; - } - } - if(fp) fclose(fp); - - return szISO; -} -#endif // 0 - static PNGRESOLUTION GetPNGResolutionBuf(void *pPngBuf, size_t nPngSize) { PNGRESOLUTION nResolution = { 0, 0 }; @@ -2028,7 +1643,7 @@ static INT_PTR CALLBACK NeoCDList_WndProc(HWND hDlg, UINT Msg, WPARAM wParam, LP { if(nSelectedItem >= 0) { GAMELIST* pEntry = &pGameLib->pGameData[nSelectedItem]; - const TCHAR* targetFile = (pEntry->bFoundCUE) ? pEntry->szPathCUE : pEntry->szPath; + const TCHAR* targetFile = pEntry->szPath; nCDEmuSelect = 0; _tcsncpy(CDEmuImage, targetFile, MAX_PATH - 1); CDEmuImage[MAX_PATH - 1]= _T('\0'); @@ -2122,7 +1737,7 @@ static INT_PTR CALLBACK NeoCDList_WndProc(HWND hDlg, UINT Msg, WPARAM wParam, LP { if(nSelectedItem >= 0) { GAMELIST* pEntry = &pGameLib->pGameData[nSelectedItem]; - const TCHAR* targetFile = (pEntry->bFoundCUE) ? pEntry->szPathCUE : pEntry->szPath; + const TCHAR* targetFile = pEntry->szPath; nCDEmuSelect = 0; _tcsncpy(CDEmuImage, targetFile, MAX_PATH - 1); CDEmuImage[MAX_PATH - 1]= _T('\0'); diff --git a/src/burner/win32/scrn.cpp b/src/burner/win32/scrn.cpp index 99c2cbab3..05f4c5f76 100644 --- a/src/burner/win32/scrn.cpp +++ b/src/burner/win32/scrn.cpp @@ -10,6 +10,24 @@ int nActiveGame; static bool bLoading = 0; +#ifdef BUILD_NEOGEO +static void SetNeoCDTitle(TCHAR* pszTitle) +{ + TCHAR szText[1024] = _T(""); + _sntprintf(szText, _countof(szText), _T(APP_TITLE) _T(" v%.20s") _T(SEPERATOR_1) _T("%s") _T(SEPERATOR_1) _T("%s"), szAppBurnVer, BurnDrvGetText(DRV_FULLNAME), pszTitle); + szText[_countof(szText) - 1] = _T('\0'); + SetWindowText(hScrnWnd, szText); +} + +void NeoCDInfo_SetTitle() +{ + if (!IsNeoGeoCD()) return; + TCHAR* pszTitle = NeoCDInfo_Text(DRV_FULLNAME); + SetNeoCDTitle(pszTitle ? pszTitle : FBALoadStringEx(hAppInst, IDS_UNIDENTIFIED_CD, true)); +} +#endif + + int OnMenuSelect(HWND, HMENU, int, HMENU, UINT); int OnInitMenuPopup(HWND, HMENU, UINT, BOOL); int OnUnInitMenuPopup(HWND, HMENU, UINT, BOOL); diff --git a/src/intf/cd/cd_chd.cpp b/src/intf/cd/cd_chd.cpp index 13b9805e6..03002b13b 100644 --- a/src/intf/cd/cd_chd.cpp +++ b/src/intf/cd/cd_chd.cpp @@ -23,6 +23,7 @@ struct ChdImage { INT32 nFramesPerHunk; UINT32 nTotalHunks; INT32 nVersion; + UINT8 Sha1[20]; INT32 nCachedHunk; // -1 = none ChdTrack Tracks[CHD_MAX_TRACKS + 1]; // +1 dummy lead-out entry }; @@ -174,16 +175,16 @@ static INT32 ChdParseToc(ChdImage* pImage) return 1; } - pT->nSubSize = ChdSubSizeFromString(szSub); - pT->nSubType = (pT->nSubSize != 0) ? 1 : 0; - pT->nFrames = nFrames; - pT->nPregap = nPregap; - pT->nPostgap = nPostgap; + pT->nSubSize = ChdSubSizeFromString(szSub); + pT->nSubType = (pT->nSubSize != 0) ? 1 : 0; + pT->nFrames = nFrames; + pT->nPregap = nPregap; + pT->nPostgap = nPostgap; pT->nPadFrames = nPad; // chdman pads each track up to a 4-frame boundary in the CHD. INT32 nPadded = (nFrames + CHD_TRACK_PADDING - 1) / CHD_TRACK_PADDING; pT->nExtraFrames = nPadded * CHD_TRACK_PADDING - nFrames; - pT->nControl = (pT->nType == CHD_TRACK_AUDIO) ? 0x01 : 0x41; + pT->nControl = (pT->nType == CHD_TRACK_AUDIO) ? 0x01 : 0x41; } if (nTrk == 0) { @@ -278,6 +279,7 @@ ChdImage* ChdOpenFile(const TCHAR* szPath) pImage->nFramesPerHunk = pImage->nHunkBytes / CHD_FRAME_SIZE; pImage->nTotalHunks = pHeader->totalhunks; pImage->nVersion = (INT32)pHeader->version; + memcpy(pImage->Sha1, pHeader->sha1, sizeof(pImage->Sha1)); pImage->nContainer = ChdDetectContainer(pImage); @@ -316,13 +318,25 @@ void ChdClose(ChdImage* pImage) free(pImage); } -INT32 ChdGetContainerType(ChdImage* pImage) { return pImage ? pImage->nContainer : CHD_CONTAINER_NONE; } -INT32 ChdGetNumTracks(ChdImage* pImage) { return pImage ? pImage->nNumTracks : 0; } -INT32 ChdGetTotalFrames(ChdImage* pImage) { return pImage ? pImage->nTotalFrames : 0; } -INT32 ChdGetVersion(ChdImage* pImage) { return pImage ? pImage->nVersion : 0; } -INT32 ChdGetHunkBytes(ChdImage* pImage) { return pImage ? pImage->nHunkBytes : 0; } +INT32 ChdGetContainerType(ChdImage* pImage) { return pImage ? pImage->nContainer : CHD_CONTAINER_NONE; } +INT32 ChdGetNumTracks(ChdImage* pImage) { return pImage ? pImage->nNumTracks : 0; } +INT32 ChdGetTotalFrames(ChdImage* pImage) { return pImage ? pImage->nTotalFrames : 0; } +INT32 ChdGetVersion(ChdImage* pImage) { return pImage ? pImage->nVersion : 0; } +INT32 ChdGetHunkBytes(ChdImage* pImage) { return pImage ? pImage->nHunkBytes : 0; } INT32 ChdGetFramesPerHunk(ChdImage* pImage) { return pImage ? pImage->nFramesPerHunk : 0; } +INT32 ChdGetSha1(ChdImage* pImage, UINT8* pSha1) +{ + if (!pImage || !pSha1 || pImage->nVersion < 3) return 1; + for (INT32 i = 0; i < (INT32)sizeof(pImage->Sha1); i++) { + if (pImage->Sha1[i]) { + memcpy(pSha1, pImage->Sha1, sizeof(pImage->Sha1)); + return 0; + } + } + return 1; +} + const ChdTrack* ChdGetTrack(ChdImage* pImage, INT32 nTrack) { if (!pImage || nTrack < 0 || nTrack >= pImage->nNumTracks) { @@ -342,7 +356,7 @@ static INT32 ChdReadFrameBytes(ChdImage* pImage, INT32 nChdFrame, INT32 nOffset, return 1; } - INT32 nHunk = nChdFrame / pImage->nFramesPerHunk; + INT32 nHunk = nChdFrame / pImage->nFramesPerHunk; INT32 nFrameInHunk = nChdFrame % pImage->nFramesPerHunk; if ((UINT32)nHunk >= pImage->nTotalHunks) { return 1; @@ -393,7 +407,7 @@ INT32 ChdReadSector(ChdImage* pImage, INT32 nLba, INT32 nDataType, UINT8* pDest) return 1; } - INT32 nTrack = 0; + INT32 nTrack = 0; INT32 nChdFrame = ChdLogicalToChd(pImage, nLba, &nTrack); if (nChdFrame < 0) { return 1; @@ -418,7 +432,7 @@ INT32 ChdReadSector(ChdImage* pImage, INT32 nLba, INT32 nDataType, UINT8* pDest) // 2352 mode-1 raw sector out of 2048 mode-1 data (synthesize sync + header) if (nDataType == CHD_TRACK_MODE1_RAW && nTrackType == CHD_TRACK_MODE1) { - static const UINT8 SyncBytes[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; + static const UINT8 SyncBytes[12] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; UINT32 nMsf = ChdLbaToMsf(nLba + 150); memset(pDest, 0, 2352); memcpy(pDest, SyncBytes, 12); @@ -430,8 +444,7 @@ INT32 ChdReadSector(ChdImage* pImage, INT32 nLba, INT32 nDataType, UINT8* pDest) } // 2048 mode-1 data out of a mode-2 form1 or raw sector - if (nDataType == CHD_TRACK_MODE1 && - (nTrackType == CHD_TRACK_MODE2_FORM1 || nTrackType == CHD_TRACK_MODE2_RAW)) { + if (nDataType == CHD_TRACK_MODE1 && (nTrackType == CHD_TRACK_MODE2_FORM1 || nTrackType == CHD_TRACK_MODE2_RAW)) { return ChdReadFrameBytes(pImage, nChdFrame, 24, 2048, pDest); } @@ -441,8 +454,7 @@ INT32 ChdReadSector(ChdImage* pImage, INT32 nLba, INT32 nDataType, UINT8* pDest) } // 2336 mode-2 data out of a 2352 raw sector (skip the header) - if (nDataType == CHD_TRACK_MODE2 && - (nTrackType == CHD_TRACK_MODE1_RAW || nTrackType == CHD_TRACK_MODE2_RAW)) { + if (nDataType == CHD_TRACK_MODE2 && (nTrackType == CHD_TRACK_MODE1_RAW || nTrackType == CHD_TRACK_MODE2_RAW)) { return ChdReadFrameBytes(pImage, nChdFrame, 16, 2336, pDest); } diff --git a/src/intf/cd/cd_chd.h b/src/intf/cd/cd_chd.h index 4c3ec113c..69adad4d3 100644 --- a/src/intf/cd/cd_chd.h +++ b/src/intf/cd/cd_chd.h @@ -7,37 +7,37 @@ // Track types (mirror libchdr cdrom.h so consumers need not include it). enum { - CHD_TRACK_MODE1 = 0, // mode 1, 2048 data bytes - CHD_TRACK_MODE1_RAW, // mode 1, 2352 raw bytes - CHD_TRACK_MODE2, // mode 2, 2336 data bytes - CHD_TRACK_MODE2_FORM1, // mode 2 form 1, 2048 data bytes - CHD_TRACK_MODE2_FORM2, // mode 2 form 2, 2324 data bytes - CHD_TRACK_MODE2_FORM_MIX, // mode 2 mixed, 2336 data bytes - CHD_TRACK_MODE2_RAW, // mode 2, 2352 raw bytes - CHD_TRACK_AUDIO, // redbook audio, 2352 bytes + CHD_TRACK_MODE1 = 0, // mode 1, 2048 data bytes + CHD_TRACK_MODE1_RAW, // mode 1, 2352 raw bytes + CHD_TRACK_MODE2, // mode 2, 2336 data bytes + CHD_TRACK_MODE2_FORM1, // mode 2 form 1, 2048 data bytes + CHD_TRACK_MODE2_FORM2, // mode 2 form 2, 2324 data bytes + CHD_TRACK_MODE2_FORM_MIX, // mode 2 mixed, 2336 data bytes + CHD_TRACK_MODE2_RAW, // mode 2, 2352 raw bytes + CHD_TRACK_AUDIO, // redbook audio, 2352 bytes - CHD_TRACK_RAW_DONTCARE // read: hand back whatever the track stores + CHD_TRACK_RAW_DONTCARE // read: hand back whatever the track stores }; // Container class, decided at open time. enum { CHD_CONTAINER_NONE = 0, - CHD_CONTAINER_CD, // CHT2 / CHTR metadata - CHD_CONTAINER_GDROM, // CHGD metadata - CHD_CONTAINER_HDD // GDDD / hard-disk metadata (e.g. CPS-2 data) + CHD_CONTAINER_CD, // CHT2 / CHTR metadata + CHD_CONTAINER_GDROM, // CHGD metadata + CHD_CONTAINER_HDD // GDDD / hard-disk metadata (e.g. CPS-2 data) }; struct ChdTrack { - INT32 nType; // CHD_TRACK_* - INT32 nSubType; // 0 none / 1 normal / 2 raw - INT32 nDataSize; // 2048 / 2324 / 2336 / 2352 - INT32 nSubSize; // 0 or 96 - INT32 nFrames; // sectors in the track (incl. pregap) - INT32 nPregap; // pregap sectors - INT32 nPostgap; // postgap sectors - INT32 nPadFrames; // GD-ROM padding sectors - INT32 nExtraFrames; // chdman 4-frame-boundary padding - INT32 nControl; // Q-channel control nibble << 4 flags (0x41 data / 0x01 audio) + INT32 nType; // CHD_TRACK_* + INT32 nSubType; // 0 none / 1 normal / 2 raw + INT32 nDataSize; // 2048 / 2324 / 2336 / 2352 + INT32 nSubSize; // 0 or 96 + INT32 nFrames; // sectors in the track (incl. pregap) + INT32 nPregap; // pregap sectors + INT32 nPostgap; // postgap sectors + INT32 nPadFrames; // GD-ROM padding sectors + INT32 nExtraFrames; // chdman 4-frame-boundary padding + INT32 nControl; // Q-channel control nibble << 4 flags (0x41 data / 0x01 audio) INT32 nPhysFrameOfs; INT32 nChdFrameOfs; INT32 nLogFrameOfs; @@ -63,6 +63,7 @@ const TCHAR* ChdTrackTypeName(INT32 nType); INT32 ChdGetVersion(ChdImage* pImage); INT32 ChdGetHunkBytes(ChdImage* pImage); INT32 ChdGetFramesPerHunk(ChdImage* pImage); +INT32 ChdGetSha1(ChdImage* pImage, UINT8* pSha1); // Total logical sectors on the disc (lead-out LBA). INT32 ChdGetTotalFrames(ChdImage* pImage); diff --git a/src/intf/cd/cd_img.cpp b/src/intf/cd/cd_img.cpp index b09b5ad92..480b22021 100644 --- a/src/intf/cd/cd_img.cpp +++ b/src/intf/cd/cd_img.cpp @@ -31,41 +31,622 @@ const INT32 CD_TYPE_BINCUE = 1 << 1; const INT32 CD_TYPE_CCD = 1 << 2; const INT32 CD_TYPE_CHD = 1 << 3; -static INT32 cd_pregap; -static double cd_volume = 100.0; +struct CDImageFile { + TCHAR szPath[MAX_PATH]; + FILE* pFile; + INT64 nSectors; +}; -struct MSF { UINT8 M; UINT8 S; UINT8 F; }; +struct CDImage { + TCHAR szPath[MAX_PATH]; + INT32 nType; + INT32 nTrackCount; + INT32 nAudioTrackCount; + INT32 nSectorCount; + INT32 nFileCount; + CDImageFile Files[MAXIMUM_NUMBER_TRACKS]; + CDImageTrack Tracks[MAXIMUM_NUMBER_TRACKS]; + INT32 nFile[MAXIMUM_NUMBER_TRACKS]; + INT32 nFileLBA[MAXIMUM_NUMBER_TRACKS]; + INT32 nIndex0FileLBA[MAXIMUM_NUMBER_TRACKS]; + INT32 nExplicitPregap[MAXIMUM_NUMBER_TRACKS]; + INT32 nChdType[MAXIMUM_NUMBER_TRACKS]; + ChdImage* pChd; +}; -struct cdimgTRACK_DATA { UINT8 Control; UINT8 TrackNumber; UINT8 Address[4]; UINT8 EndAddress[4]; }; -struct cdimgCDROM_TOC { UINT8 FirstTrack; UINT8 LastTrack; UINT8 ImageType; TCHAR Image[MAX_PATH]; cdimgTRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS + 1]; }; +static INT32 cdimgCopyPath(TCHAR* dst, size_t count, const TCHAR* src); +static INT32 cdimgReplaceExtension(TCHAR* path, size_t count, const TCHAR* extension); + +static INT32 CDImageParseMSF(const TCHAR* szText, INT32* pnLba) +{ + TCHAR* pEnd; + INT32 nMinute = _tcstol(szText, &pEnd, 10); + if (pEnd == szText || *pEnd++ != _T(':')) + return 1; + + TCHAR* pSecond = pEnd; + INT32 nSecond = _tcstol(pSecond, &pEnd, 10); + if (pEnd == pSecond || *pEnd++ != _T(':')) + return 1; + + TCHAR* pFrame = pEnd; + INT32 nFrame = _tcstol(pFrame, &pEnd, 10); + while (*pEnd == _T(' ') || *pEnd == _T('\t')) + pEnd++; + + if (pEnd == pFrame || *pEnd || nMinute < 0 || nSecond < 0 || nSecond > 59 || nFrame < 0 || nFrame > 74) + return 1; + + *pnLba = nMinute * CD_FRAMES_MINUTE + nSecond * CD_FRAMES_SECOND + nFrame; + return 0; +} + +static INT32 CDImageResolvePath(TCHAR* szDest, const TCHAR* szImage, const TCHAR* szFile) +{ + if (!szDest || !szImage || !szFile) + return 1; + + if (szFile[0] == _T('/') || szFile[0] == _T('\\') || (_tcslen(szFile) > 1 && szFile[1] == _T(':'))) { + return cdimgCopyPath(szDest, MAX_PATH, szFile); + } + const TCHAR* pSlash = _tcsrchr(szImage, _T('/')); + const TCHAR* pBackslash = _tcsrchr(szImage, _T('\\')); + if (!pSlash || (pBackslash && pBackslash > pSlash)) + pSlash = pBackslash; + + size_t nDirectory = pSlash ? (size_t)(pSlash - szImage + 1) : 0; + if (nDirectory + _tcslen(szFile) >= MAX_PATH) + return 1; + + if (nDirectory) + memcpy(szDest, szImage, nDirectory * sizeof(TCHAR)); + + _tcscpy(szDest + nDirectory, szFile); + return 0; +} + +static void CDImageCloseFiles(CDImage* pImage) +{ + if (!pImage) + return; + + for (INT32 i = 0; i < pImage->nFileCount; i++) { + if (pImage->Files[i].pFile) + fclose(pImage->Files[i].pFile); + pImage->Files[i].pFile = NULL; + } +} + +static INT32 CDImageOpenBinary(CDImageFile* pFile) +{ + pFile->pFile = _tfopen(pFile->szPath, _T("rb")); + if (!pFile->pFile) + return 1; + + if (cdimgFseek(pFile->pFile, 0, SEEK_END)) { + fclose(pFile->pFile); + pFile->pFile = NULL; + return 1; + } + INT64 nSize = cdimgFtell(pFile->pFile); + if (nSize < 0 || cdimgFseek(pFile->pFile, 0, SEEK_SET)) { + fclose(pFile->pFile); + pFile->pFile = NULL; + return 1; + } + pFile->nSectors = nSize; + return 0; +} + +static INT32 CDImageParseCue(CDImage* pImage) +{ + FILE* pCue = _tfopen(pImage->szPath, _T("rt")); + if (!pCue) + return 1; + + TCHAR szLine[1024]; + INT32 nFile = -1; + INT32 nTrack = -1; + INT32 nDiscBase = 0; + INT32 nCurrentFile = -1; + INT32 nFileBase = 0; + INT32 nVirtualGap = 0; + UINT8 Index1Seen[MAXIMUM_NUMBER_TRACKS] = { 0 }; + while (_fgetts(szLine, ARRAY_SIZE(szLine), pCue)) { + INT32 nLength = (INT32)_tcslen(szLine); + while (nLength && (szLine[nLength - 1] == _T('\r') || szLine[nLength - 1] == _T('\n'))) + szLine[--nLength] = 0; + + TCHAR* p = szLine; + TCHAR* pLabel; + if ((pLabel = LabelCheck(p, _T("FILE"))) != NULL) { + TCHAR* pName = NULL; + TCHAR* pEnd = NULL; + if (pImage->nFileCount >= MAXIMUM_NUMBER_TRACKS || QuoteRead(&pName, &pEnd, pLabel) || !pName || !pEnd) + goto fail; + + while (*pEnd == _T(' ') || *pEnd == _T('\t')) + pEnd++; + if (_tcsncmp(pEnd, _T("BINARY"), 6)) + goto fail; + + pEnd += 6; + while (*pEnd == _T(' ') || *pEnd == _T('\t')) + pEnd++; + if (*pEnd) + goto fail; + + nFile = pImage->nFileCount++; + if (CDImageResolvePath(pImage->Files[nFile].szPath, pImage->szPath, pName) || CDImageOpenBinary(&pImage->Files[nFile])) + goto fail; + + continue; + } + if ((pLabel = LabelCheck(p, _T("TRACK"))) != NULL) { + if (nFile < 0 || pImage->nTrackCount >= MAXIMUM_NUMBER_TRACKS) + goto fail; + TCHAR* pEnd; + INT32 nNumber = _tcstol(pLabel, &pEnd, 10); + if (nNumber != pImage->nTrackCount + 1) + goto fail; + + while (*pEnd == _T(' ') || *pEnd == _T('\t')) pEnd++; + nTrack = pImage->nTrackCount++; + CDImageTrack* pTrack = &pImage->Tracks[nTrack]; + pTrack->nNumber = nNumber; + pTrack->nIndex0LBA = -1; + pTrack->nControl = 0x41; + if (!_tcscmp(pEnd, _T("AUDIO"))) { + pTrack->nType = CDIMAGE_TRACK_AUDIO; + pTrack->nControl = 0x01; + pTrack->nSectorSize = pTrack->nUserSize = 2352; + pImage->nAudioTrackCount++; + } else if (!_tcscmp(pEnd, _T("MODE1/2048"))) { + pTrack->nType = CDIMAGE_TRACK_MODE1_2048; + pTrack->nSectorSize = pTrack->nUserSize = 2048; + pTrack->nUserOffset = 16; + } else if (!_tcscmp(pEnd, _T("MODE1/2352"))) { + pTrack->nType = CDIMAGE_TRACK_MODE1_2352; + pTrack->nSectorSize = 2352; + pTrack->nUserOffset = 16; + pTrack->nUserSize = 2048; + } else if (!_tcscmp(pEnd, _T("MODE2/2336"))) { + pTrack->nType = CDIMAGE_TRACK_MODE2_2336; + pTrack->nSectorSize = pTrack->nUserSize = 2336; + pTrack->nUserOffset = 16; + } else if (!_tcscmp(pEnd, _T("MODE2/2352")) || !_tcscmp(pEnd, _T("CDI/2352"))) { + pTrack->nType = CDIMAGE_TRACK_MODE2_2352; + pTrack->nSectorSize = 2352; + pTrack->nUserOffset = 16; + pTrack->nUserSize = 2336; + } else + goto fail; + + pImage->nFile[nTrack] = nFile; + pTrack->szPath = pImage->Files[nFile].szPath; + continue; + } + if ((pLabel = LabelCheck(p, _T("INDEX 00"))) != NULL) { + if (nTrack < 0 || CDImageParseMSF(pLabel, &pImage->nIndex0FileLBA[nTrack])) + goto fail; + + continue; + } + if ((pLabel = LabelCheck(p, _T("INDEX 01"))) != NULL) { + if (nTrack < 0 || Index1Seen[nTrack] || CDImageParseMSF(pLabel, &pImage->nFileLBA[nTrack])) + goto fail; + + Index1Seen[nTrack] = 1; + continue; + } + if ((pLabel = LabelCheck(p, _T("PREGAP"))) != NULL) { + if (nTrack < 0 || CDImageParseMSF(pLabel, &pImage->nExplicitPregap[nTrack])) + goto fail; + } + } + fclose(pCue); + if (!pImage->nTrackCount) + return 1; + + for (INT32 i = 0; i < pImage->nTrackCount; i++) + if (!Index1Seen[i]) + return 1; + + for (INT32 i = 0; i < pImage->nFileCount; i++) { + INT32 nSectorSize = 0; + for (INT32 j = 0; j < pImage->nTrackCount; j++) { + if (pImage->nFile[j] != i) + continue; + if (nSectorSize && nSectorSize != pImage->Tracks[j].nSectorSize) + return 1; + nSectorSize = pImage->Tracks[j].nSectorSize; + } + + if (!nSectorSize || pImage->Files[i].nSectors % nSectorSize) + return 1; + + pImage->Files[i].nSectors /= nSectorSize; + } + for (INT32 i = 0; i < pImage->nTrackCount; i++) { + if (pImage->nFile[i] != nCurrentFile) { + nCurrentFile = pImage->nFile[i]; + nFileBase = nDiscBase; + nVirtualGap = 0; + } + nVirtualGap += pImage->nExplicitPregap[i]; + CDImageTrack* pTrack = &pImage->Tracks[i]; + pTrack->nIndex1LBA = nFileBase + pImage->nFileLBA[i] + nVirtualGap; + pTrack->nStartLBA = pTrack->nIndex1LBA; + if (pImage->nIndex0FileLBA[i] >= 0) + pTrack->nIndex0LBA = nFileBase + pImage->nIndex0FileLBA[i] + nVirtualGap; + else + pTrack->nIndex0LBA = pTrack->nIndex1LBA - pImage->nExplicitPregap[i]; + + pTrack->nPregap = pTrack->nIndex1LBA - pTrack->nIndex0LBA; + nDiscBase = nFileBase + (INT32)pImage->Files[nCurrentFile].nSectors + nVirtualGap; + } + pImage->nSectorCount = nDiscBase; + for (INT32 i = 0; i < pImage->nTrackCount; i++) { + INT32 nEnd = i + 1 < pImage->nTrackCount ? pImage->Tracks[i + 1].nIndex0LBA : pImage->nSectorCount; + pImage->Tracks[i].nSectors = nEnd - pImage->Tracks[i].nStartLBA; + } + return 0; + +fail: + fclose(pCue); + return 1; +} + +static inline UINT8 CDImageBcd(UINT8 nValue) +{ + return ((nValue >> 4) * 10) + (nValue & 15); +} + +static INT32 CDImageParseCcd(CDImage* pImage) +{ + TCHAR szSub[MAX_PATH]; + if (cdimgCopyPath(szSub, MAX_PATH, pImage->szPath) || cdimgReplaceExtension(szSub, MAX_PATH, _T(".sub"))) + return 1; + if (pImage->nFileCount >= 1 || cdimgCopyPath(pImage->Files[0].szPath, MAX_PATH, pImage->szPath) || cdimgReplaceExtension(pImage->Files[0].szPath, MAX_PATH, _T(".img")) || CDImageOpenBinary(&pImage->Files[0])) + return 1; + + pImage->nFileCount = 1; + FILE* pSub = _tfopen(szSub, _T("rb")); + if (!pSub || cdimgFseek(pSub, 0, SEEK_END)) { + if (pSub) + fclose(pSub); + + return 1; + } + INT64 nSize = cdimgFtell(pSub); + if (nSize < 96 || nSize % 96 || cdimgFseek(pSub, 0, SEEK_SET)) { + fclose(pSub); + return 1; + } + UINT8 Sub[96]; + INT32 nLastTrack = 0; + INT32 nIndex1Base = -1; + while (fread(Sub, 1, sizeof(Sub), pSub) == sizeof(Sub)) { + UINT8* pQ = Sub + 12; + INT32 nAbsolute = CDImageBcd(pQ[7]) * CD_FRAMES_MINUTE + CDImageBcd(pQ[8]) * CD_FRAMES_SECOND + CDImageBcd(pQ[9]); + INT32 nTrack = CDImageBcd(pQ[1]); + INT32 nIndex = CDImageBcd(pQ[2]); + if (nIndex == 1 && nTrack == nLastTrack + 1 && nTrack <= MAXIMUM_NUMBER_TRACKS) { + CDImageTrack* pTrack = &pImage->Tracks[nLastTrack]; + pTrack->nNumber = nTrack; pTrack->nControl = pQ[0]; pTrack->nType = (pQ[0] & 0x40) ? CDIMAGE_TRACK_MODE1_2352 : CDIMAGE_TRACK_AUDIO; + pTrack->nSectorSize = 2352; pTrack->nUserOffset = pTrack->nType == CDIMAGE_TRACK_AUDIO ? 0 : 16; pTrack->nUserSize = pTrack->nType == CDIMAGE_TRACK_AUDIO ? 2352 : 2048; + if (nIndex1Base < 0) + nIndex1Base = nAbsolute; + pTrack->nStartLBA = pTrack->nIndex1LBA = nAbsolute - nIndex1Base; + pTrack->nIndex0LBA = pTrack->nStartLBA; pTrack->szPath = pImage->Files[0].szPath; pImage->nFile[nLastTrack] = 0; pImage->nFileLBA[nLastTrack] = nAbsolute - nIndex1Base; + if (pTrack->nType == CDIMAGE_TRACK_AUDIO) + pImage->nAudioTrackCount++; + + nLastTrack++; + } + } + fclose(pSub); + pImage->nTrackCount = nLastTrack; + pImage->Files[0].nSectors /= 2352; + pImage->nSectorCount = (INT32)pImage->Files[0].nSectors; + for (INT32 i = 0; i < nLastTrack; i++) + pImage->Tracks[i].nSectors = (i + 1 < nLastTrack ? pImage->Tracks[i + 1].nStartLBA : pImage->nSectorCount) - pImage->Tracks[i].nStartLBA; + + return nLastTrack ? 0 : 1; +} + +static INT32 CDImageParseRaw(CDImage* pImage) +{ + pImage->nFileCount = 1; + if (cdimgCopyPath(pImage->Files[0].szPath, MAX_PATH, pImage->szPath) || CDImageOpenBinary(&pImage->Files[0]) || pImage->Files[0].nSectors % 2352) + return 1; + + pImage->Files[0].nSectors /= 2352; + pImage->nTrackCount = 1; + pImage->nSectorCount = (INT32)pImage->Files[0].nSectors; + + CDImageTrack* pTrack = &pImage->Tracks[0]; + pTrack->nNumber = 1; + pTrack->nType = CDIMAGE_TRACK_MODE1_2352; + pTrack->nControl = 0x41; + pTrack->nIndex0LBA = pTrack->nIndex1LBA = pTrack->nStartLBA = 0; + pTrack->nSectors = pImage->nSectorCount; + pTrack->nSectorSize = 2352; + pTrack->nUserOffset = 16; + pTrack->nUserSize = 2048; + pTrack->szPath = pImage->Files[0].szPath; + return 0; +} + +static INT32 CDImageParseChd(CDImage* pImage) +{ + pImage->pChd = ChdOpenFile(pImage->szPath); + if (!pImage->pChd || (ChdGetContainerType(pImage->pChd) != CHD_CONTAINER_CD && ChdGetContainerType(pImage->pChd) != CHD_CONTAINER_GDROM)) + return 1; + + pImage->nTrackCount = ChdGetNumTracks(pImage->pChd); + pImage->nSectorCount = ChdGetTotalFrames(pImage->pChd); + if (pImage->nTrackCount < 1 || pImage->nTrackCount > MAXIMUM_NUMBER_TRACKS) + return 1; + + for (INT32 i = 0; i < pImage->nTrackCount; i++) { + const ChdTrack* pChdTrack = ChdGetTrack(pImage->pChd, i); + CDImageTrack* pTrack = &pImage->Tracks[i]; + pTrack->nNumber = i + 1; pTrack->nControl = pChdTrack->nControl; pTrack->nStartLBA = pTrack->nIndex1LBA = pChdTrack->nLogFrameOfs; + pTrack->nIndex0LBA = pTrack->nIndex1LBA - pChdTrack->nPregap; pTrack->nPregap = pChdTrack->nPregap; pTrack->nSectors = pChdTrack->nLogFrames; + pTrack->nSectorSize = pChdTrack->nDataSize; pTrack->szPath = pImage->szPath; + + pImage->nChdType[i] = pChdTrack->nType; + if (pChdTrack->nType == CHD_TRACK_AUDIO) { + pTrack->nType = CDIMAGE_TRACK_AUDIO; + pTrack->nUserSize = 2352; + pImage->nAudioTrackCount++; + } else if (pChdTrack->nType == CHD_TRACK_MODE1) { + pTrack->nType = CDIMAGE_TRACK_MODE1_2048; + pTrack->nUserSize = 2048; + } else if (pChdTrack->nType == CHD_TRACK_MODE1_RAW) { + pTrack->nType = CDIMAGE_TRACK_MODE1_2352; + pTrack->nUserOffset = 16; + pTrack->nUserSize = 2048; + } else if (pChdTrack->nType == CHD_TRACK_MODE2) { + pTrack->nType = CDIMAGE_TRACK_MODE2_2336; + pTrack->nUserSize = 2336; + } else { + pTrack->nType = CDIMAGE_TRACK_MODE2_2352; + pTrack->nUserOffset = pChdTrack->nType == CHD_TRACK_MODE2_RAW ? 16 : 0; + pTrack->nUserSize = pChdTrack->nDataSize; } + } + for (INT32 i = 0; i < pImage->nTrackCount; i++) { + pImage->Tracks[i].nSectors = (i + 1 < pImage->nTrackCount ? pImage->Tracks[i + 1].nStartLBA : pImage->nSectorCount) - pImage->Tracks[i].nStartLBA; + } + return 0; +} + +CDImage* CDImageOpen(const TCHAR* szPath) +{ + if (!szPath || !*szPath) + return NULL; + + CDImage* pImage = (CDImage*)calloc(1, sizeof(CDImage)); + if (!pImage || cdimgCopyPath(pImage->szPath, MAX_PATH, szPath)) { + free(pImage); + return NULL; + } + + INT32 nError = 1; + if (IsFileExt(pImage->szPath, _T(".cue"))) { + pImage->nType = CD_TYPE_BINCUE; + nError = CDImageParseCue(pImage); + } else if (IsFileExt(pImage->szPath, _T(".ccd"))) { + pImage->nType = CD_TYPE_CCD; + nError = CDImageParseCcd(pImage); + } else if (IsFileExt(pImage->szPath, _T(".chd"))) { + pImage->nType = CD_TYPE_CHD; + nError = CDImageParseChd(pImage); + } else if (IsFileExt(pImage->szPath, _T(".bin")) || IsFileExt(pImage->szPath, _T(".img"))) { + pImage->nType = CD_TYPE_BINCUE; + nError = CDImageParseRaw(pImage); + } + if (nError) { + CDImageClose(pImage); + return NULL; + } + return pImage; +} + +void CDImageClose(CDImage* pImage) +{ + if (!pImage) + return; + + CDImageCloseFiles(pImage); + if (pImage->pChd) + ChdClose(pImage->pChd); + + free(pImage); +} + +INT32 CDImageGetTrackCount(const CDImage* pImage) +{ + return pImage ? pImage->nTrackCount : 0; +} + +INT32 CDImageGetAudioTrackCount(const CDImage* pImage) +{ + return pImage ? pImage->nAudioTrackCount : 0; +} + +INT32 CDImageGetSectorCount(const CDImage* pImage) +{ + return pImage ? pImage->nSectorCount : 0; +} + +const TCHAR* CDImageGetPath(const CDImage* pImage) +{ + return pImage ? pImage->szPath : NULL; +} + +const CDImageTrack* CDImageGetTrack(const CDImage* pImage, INT32 nTrack) +{ + return pImage && nTrack >= 0 && nTrack < pImage->nTrackCount ? &pImage->Tracks[nTrack] : NULL; +} + +static INT32 CDImageFindTrack(const CDImage* pImage, INT32 nLba) +{ + if (!pImage || nLba < 0 || nLba >= pImage->nSectorCount) + return -1; + + for (INT32 i = pImage->nTrackCount - 1; i >= 0; i--) + if (nLba >= pImage->Tracks[i].nIndex0LBA) + return i; + + return -1; +} + +INT32 CDImageReadRawSector(CDImage* pImage, INT32 nLba, UINT8* pDest) +{ + if (!pImage || !pDest) + return 1; + + INT32 nTrack = CDImageFindTrack(pImage, nLba); + if (nTrack < 0) + return 1; + + CDImageTrack* pTrack = &pImage->Tracks[nTrack]; + if (pImage->pChd) { + INT32 nType = pImage->nChdType[nTrack]; + if (nType == CHD_TRACK_AUDIO) + return ChdReadSector(pImage->pChd, nLba, CHD_TRACK_RAW_DONTCARE, pDest); + if (nType == CHD_TRACK_MODE1 || nType == CHD_TRACK_MODE1_RAW) + return ChdReadSector(pImage->pChd, nLba, CHD_TRACK_MODE1_RAW, pDest); + if (nType == CHD_TRACK_MODE2_RAW) + return ChdReadSector(pImage->pChd, nLba, CHD_TRACK_RAW_DONTCARE, pDest); + + memset(pDest, 0, 2352); + INT32 nOffset = nType == CHD_TRACK_MODE2_FORM1 || nType == CHD_TRACK_MODE2_FORM2 ? 24 : 16; + return ChdReadSector(pImage->pChd, nLba, CHD_TRACK_RAW_DONTCARE, pDest + nOffset); + } + if (nLba < pTrack->nIndex1LBA && pImage->nExplicitPregap[nTrack]) { + memset(pDest, 0, 2352); + return 0; + } + INT32 nFileLba = pImage->nFileLBA[nTrack] + nLba - pTrack->nIndex1LBA; + if (nFileLba < 0) + return 1; + + CDImageFile* pFile = &pImage->Files[pImage->nFile[nTrack]]; + if (cdimgFseek(pFile->pFile, (INT64)nFileLba * pTrack->nSectorSize, SEEK_SET)) + return 1; + + if (pTrack->nSectorSize == 2352) + return fread(pDest, 1, 2352, pFile->pFile) == 2352 ? 0 : 1; + + memset(pDest, 0, 2352); + return fread(pDest + 16, 1, pTrack->nSectorSize, pFile->pFile) == (size_t)pTrack->nSectorSize ? 0 : 1; +} + +INT32 CDImageReadUserSector(CDImage* pImage, INT32 nLba, UINT8* pDest, INT32* pnSize) +{ + if (!pImage || !pDest) + return 1; + + INT32 nTrack = CDImageFindTrack(pImage, nLba); + if (nTrack < 0) + return 1; + + CDImageTrack* pTrack = &pImage->Tracks[nTrack]; + if (pTrack->nType == CDIMAGE_TRACK_AUDIO) + return 1; + + UINT8 Raw[2352]; + if (pImage->pChd) { + INT32 nType = pImage->nChdType[nTrack]; + if (pTrack->nUserSize == 2048 && (nType == CHD_TRACK_MODE1 || nType == CHD_TRACK_MODE1_RAW)) + nType = CHD_TRACK_MODE1; + else if (pTrack->nUserSize == 2336) + nType = CHD_TRACK_MODE2; + else + nType = CHD_TRACK_RAW_DONTCARE; + + if (ChdReadSector(pImage->pChd, nLba, nType, pDest)) + return 1; + } else { + if (CDImageReadRawSector(pImage, nLba, Raw)) + return 1; + + memcpy(pDest, Raw + pTrack->nUserOffset, pTrack->nUserSize); + } + if (pnSize) + *pnSize = pTrack->nUserSize; + + return 0; +} + +INT32 CDImageGetChdSha1(const CDImage* pImage, UINT8* pSha1) +{ + return pImage && pImage->pChd ? ChdGetSha1(pImage->pChd, pSha1) : 1; +} + +static CDImage* cdimgImage = NULL; +static INT32 cd_pregap; +static double cd_volume = 100.0; + +struct MSF { + UINT8 M; + UINT8 S; + UINT8 F; +}; + +struct cdimgTRACK_DATA { + UINT8 Control; + UINT8 TrackNumber; + UINT8 Address[4]; + UINT8 EndAddress[4]; +}; + +struct cdimgCDROM_TOC { + UINT8 FirstTrack; + UINT8 LastTrack; + UINT8 ImageType; + TCHAR Image[MAX_PATH]; + cdimgTRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS + 1]; +}; static cdimgCDROM_TOC* cdimgTOC; -static FILE* cdimgFile = NULL; -static ChdImage* cdimgChd = NULL; -static INT32 cdimgChdTrackLBA[MAXIMUM_NUMBER_TRACKS]; // logical LBA of each track start -static INT32 cdimgTrack = 0; -static INT32 cdimgLBA = 0; +static FILE* cdimgFile = NULL; +static ChdImage* cdimgChd = NULL; +static INT32 cdimgChdTrackLBA[MAXIMUM_NUMBER_TRACKS]; // logical LBA of each track start +static INT32 cdimgTrack = 0; +static INT32 cdimgLBA = 0; -static INT32 cdimgSamples = 0; +static INT32 cdimgSamples = 0; -static INT32 re_sync = 0; +static INT32 re_sync = 0; // identical to the format used in clonecd .sub files, can use memcpy -struct QData { UINT8 Control; char track; char index; MSF MSFrel; char unused; MSF MSFabs; UINT16 CRC; }; +struct QData { + UINT8 Control; + char track; + char index; + MSF MSFrel; + char unused; + MSF MSFabs; + UINT16 CRC; +}; -static QData* QChannel = NULL; -static INT32 QChannelCount = 0; +static QData* QChannel = NULL; +static INT32 QChannelCount = 0; // ----------------------------------------------------------------------------- -const INT32 cdimgOUT_SIZE = 2352; +const INT32 cdimgOUT_SIZE = 2352; static INT32 cdimgOutputbufferSize = 0; -static INT16* cdimgOutputbuffer = NULL; +static INT16* cdimgOutputbuffer = NULL; static INT32 cdimgOutputPosition; -static INT32 cdimgAudioFilePos; // Track audio read position within file (file sector index) +static INT32 cdimgAudioFilePos; // Track audio read position within file (file sector index) // ----------------------------------------------------------------------------- @@ -76,6 +657,9 @@ static INT32 cdimgAudioFilePos; // Track audio read position within file (file #ifdef BUILD_NEOGEO TCHAR* GetIsoPath() { + if (cdimgImage) { + return (TCHAR*)CDImageGetPath(cdimgImage); + } if (cdimgTOC) { return cdimgTOC->Image; } @@ -165,9 +749,8 @@ static INT32 cdimgInitStream() static INT32 cdimgCopyPath(TCHAR* dst, size_t count, const TCHAR* src) { - if (!dst || !src || count == 0 || _tcslen(src) >= count) { + if (!dst || !src || count == 0 || _tcslen(src) >= count) return 1; - } _tcscpy(dst, src); return 0; @@ -177,14 +760,12 @@ static INT32 cdimgReplaceExtension(TCHAR* path, size_t count, const TCHAR* exten { size_t length; - if (!path || !extension || count == 0) { + if (!path || !extension || count == 0) return 1; - } length = _tcslen(path); - if (length < 4 || _tcslen(extension) != 4) { + if (length < 4 || _tcslen(extension) != 4) return 1; - } _tcscpy(path + length - 4, extension); return 0; @@ -211,7 +792,7 @@ static void cdimgPrintImageInfo() const UINT8* addressUNBCD = dinkLBAToMSF(cdimgMSFToLBA(cdimgTOC->TrackData[trk].Address)); if (trk != cdimgTOC->LastTrack) { - bprintf(0, _T("Track %02d: %02d:%02d:%02d\n"), trk + 1, addressUNBCD[1], addressUNBCD[2], addressUNBCD[3]); + bprintf(0, _T("Track %02d: %02d:%02d:%02d\n"), trk + 1, addressUNBCD[1], addressUNBCD[2], addressUNBCD[3]); } else { bprintf(0, _T(" total running time %02i:%02i:%02i\n"), addressUNBCD[1], addressUNBCD[2], addressUNBCD[3]); } @@ -247,10 +828,10 @@ static INT32 cdimgAddLastTrack() // parse .sub file and build a TOC based in Q sub channel data static INT32 cdimgParseSubFile() { - TCHAR filenameSub[MAX_PATH]; - FILE* h = NULL; + TCHAR filenameSub[MAX_PATH]; + FILE* h = NULL; UINT8* subData = NULL; - INT32 track = 0; + INT32 track = 0; if (cdimgCopyPath(filenameSub, MAX_PATH, CDEmuImage) || (!IsFileExt(filenameSub, _T(".ccd")) && !IsFileExt(filenameSub, _T(".img")) && !IsFileExt(filenameSub, _T(".sub"))) || @@ -268,7 +849,9 @@ static INT32 cdimgParseSubFile() h = _tfopen(filenameSub, _T("rb")); if (!h || cdimgFseek(h, 0, SEEK_END) != 0) { - if (h) fclose(h); + if (h) + fclose(h); + return 1; } @@ -279,18 +862,19 @@ static INT32 cdimgParseSubFile() } QChannelCount = (INT32)(size64 / 96); - subData = (UINT8*)malloc((size_t)size64); + subData = (UINT8*)malloc((size_t)size64); QChannel = (QData*)calloc(QChannelCount, sizeof(QData)); if (!subData || !QChannel || fread(subData, 1, (size_t)size64, h) != (size_t)size64) { fclose(h); free_s((void**)&subData); - free_s((void**)&QChannel); + free(QChannel); + QChannel = NULL; QChannelCount = 0; return 1; } fclose(h); - cdimgTOC->ImageType = CD_TYPE_CCD; + cdimgTOC->ImageType = CD_TYPE_CCD; cdimgTOC->FirstTrack = 1; for (INT32 i = 0; i < QChannelCount; i++) { memcpy(&QChannel[i], subData + i * 96 + 12, 12); @@ -298,17 +882,18 @@ static INT32 cdimgParseSubFile() if (QChannel[i].index && (QChannel[i].Control & 1) && current >= 1 && current <= MAXIMUM_NUMBER_TRACKS && current == track + 1 && cdimgTOC->TrackData[current - 1].TrackNumber == 0) { track = current; - cdimgTOC->TrackData[track - 1].Control = QChannel[i].Control; + cdimgTOC->TrackData[track - 1].Control = QChannel[i].Control; cdimgTOC->TrackData[track - 1].TrackNumber = (UINT8)QChannel[i].track; - cdimgTOC->TrackData[track - 1].Address[1] = QChannel[i].MSFabs.M; - cdimgTOC->TrackData[track - 1].Address[2] = QChannel[i].MSFabs.S; - cdimgTOC->TrackData[track - 1].Address[3] = QChannel[i].MSFabs.F; + cdimgTOC->TrackData[track - 1].Address[1] = QChannel[i].MSFabs.M; + cdimgTOC->TrackData[track - 1].Address[2] = QChannel[i].MSFabs.S; + cdimgTOC->TrackData[track - 1].Address[3] = QChannel[i].MSFabs.F; } } free_s((void**)&subData); if (track == 0) { - free_s((void**)&QChannel); + free(QChannel); + QChannel = NULL; QChannelCount = 0; return 1; } @@ -320,15 +905,15 @@ static INT32 cdimgParseSubFile() static INT32 cdimgParseCueFile() { - TCHAR szLine[1024]; - TCHAR szFile[MAX_PATH]; + TCHAR szLine[1024]; + TCHAR szFile[MAX_PATH]; TCHAR* s; TCHAR* t; - FILE* h; - INT32 track = 0; - INT32 length; - INT32 fileCount = 0; - UINT8 indexSeen[MAXIMUM_NUMBER_TRACKS] = { 0 }; + FILE* h; + INT32 track = 0; + INT32 length; + INT32 fileCount = 0; + UINT8 indexSeen[MAXIMUM_NUMBER_TRACKS] = { 0 }; cdimgTOC->ImageType = CD_TYPE_BINCUE; cdimgTOC->FirstTrack = 1; @@ -338,7 +923,7 @@ static INT32 cdimgParseCueFile() cdimgTOC->TrackData[0].Address[2] = 2; cdimgTOC->TrackData[0].Address[3] = 0; - cd_pregap = 150; // default for bin/cue? + cd_pregap = 150; // default for bin/cue? if (cdimgCopyPath(cdimgTOC->Image, MAX_PATH, CDEmuImage) || cdimgReplaceExtension(cdimgTOC->Image, MAX_PATH, _T(".bin"))) { return 1; @@ -368,7 +953,7 @@ static INT32 cdimgParseCueFile() s = t; TCHAR* szQuote = NULL; - TCHAR* szEnd = NULL; + TCHAR* szEnd = NULL; const TCHAR* separator = _tcsrchr(CDEmuImage, _T('/')); const TCHAR* backslash = _tcsrchr(CDEmuImage, _T('\\')); if (!separator || (backslash && backslash > separator)) separator = backslash; @@ -390,14 +975,16 @@ static INT32 cdimgParseCueFile() return 1; } - bool absolute = szQuote[0] == _T('/') || szQuote[0] == _T('\\') || - (_tcslen(szQuote) > 1 && szQuote[1] == _T(':')); - if (absolute) directoryLength = 0; + bool absolute = szQuote[0] == _T('/') || szQuote[0] == _T('\\') || (_tcslen(szQuote) > 1 && szQuote[1] == _T(':')); + if (absolute) + directoryLength = 0; if (directoryLength + _tcslen(szQuote) >= MAX_PATH) { fclose(h); return 1; } - if (directoryLength) memcpy(szFile, CDEmuImage, directoryLength * sizeof(TCHAR)); + if (directoryLength) + memcpy(szFile, CDEmuImage, directoryLength * sizeof(TCHAR)); + _tcscpy(szFile + directoryLength, szQuote); if (cdimgCopyPath(cdimgTOC->Image, MAX_PATH, szFile)) { fclose(h); @@ -417,12 +1004,11 @@ static INT32 cdimgParseCueFile() fclose(h); return 1; } - if (track < cdimgTOC->FirstTrack) { cdimgTOC->FirstTrack = track; } if (track > cdimgTOC->LastTrack) { - cdimgTOC->LastTrack = track; + cdimgTOC->LastTrack = track; } cdimgTOC->TrackData[track - 1].TrackNumber = tobcd(track); @@ -476,7 +1062,7 @@ static INT32 cdimgParseCueFile() return 1; } - const UINT8 address[] = { 0, (UINT8)M, (UINT8)S, (UINT8)F }; + const UINT8 address[] = { 0, (UINT8)M, (UINT8)S, (UINT8)F }; const UINT8* newaddress = cdimgLBAToMSF(dinkMSFToLBA(address) + cd_pregap); //const UINT8* newaddressUNBCD = dinkLBAToMSF(dinkMSFToLBA(address) + cd_pregap); //bprintf(0, _T("Track MSF: %02d:%02d:%02d "), newaddressUNBCD[1], newaddressUNBCD[2], newaddressUNBCD[3]); @@ -510,19 +1096,23 @@ static INT32 cdimgExit() if (cdimgFile) fclose(cdimgFile); - cdimgFile = NULL; + cdimgFile = NULL; if (cdimgChd) ChdClose(cdimgChd); - cdimgChd = NULL; + + cdimgChd = NULL; + CDImageClose(cdimgImage); + cdimgImage = NULL; cdimgTrack = 0; cdimgLBA = 0; cd_volume = 100.0; - free_s((void**)&cdimgTOC); - free_s((void**)&QChannel); - QChannel = NULL; + free(cdimgTOC); + cdimgTOC = NULL; + free(QChannel); + QChannel = NULL; QChannelCount = 0; return 0; @@ -544,17 +1134,13 @@ static INT32 cdimgReadRawSector(INT32 lba, UINT8* dest, bool bAudio = false) { if (!cdimgTOC) return 1; - - if (cdimgTOC->ImageType == CD_TYPE_CHD) { - INT32 nType = bAudio ? CHD_TRACK_RAW_DONTCARE : CHD_TRACK_MODE1_RAW; - return ChdReadSector(cdimgChd, lba, nType, dest); - } - - // Fall-through: standard raw image (.bin/.cue, .ccd/.img). + if (cdimgImage) + return CDImageReadRawSector(cdimgImage, lba, dest); if (!cdimgFile) return 1; if (cdimgFseek(cdimgFile, (INT64)lba * 2352, SEEK_SET) != 0) return 1; + size_t n = fread(dest, 1, 2352, cdimgFile); return (n == 2352) ? 0 : 1; } @@ -571,6 +1157,56 @@ INT32 cdimgCountChdAudioTracks(TCHAR* pszFile) return ChdCountAudioTracks(pszPath); } +static INT32 cdimgParseImage() +{ + INT32 nType = IsFileExt(CDEmuImage, _T(".chd")) ? CD_TYPE_CHD : IsFileExt(CDEmuImage, _T(".ccd")) ? CD_TYPE_CCD : CD_TYPE_BINCUE; + + cdimgImage = CDImageOpen(CDEmuImage); + if (!cdimgImage) + return 1; + + INT32 nTracks = CDImageGetTrackCount(cdimgImage); + if (nTracks < 1 || nTracks > MAXIMUM_NUMBER_TRACKS) + return 1; + + if (nType == CD_TYPE_CCD) { + INT32 nError = cdimgParseSubFile(); + if (nError) + return nError; + + for (INT32 i = 0; i < nTracks; i++) { + const CDImageTrack* pTrack = CDImageGetTrack(cdimgImage, i); + cdimgTOC->TrackData[i].EndAddress[0] = 0; + const UINT8* pAddress = cdimgLBAToMSF((i + 1 < nTracks ? CDImageGetTrack(cdimgImage, i + 1)->nIndex0LBA : CDImageGetSectorCount(cdimgImage)) + cd_pregap); + memcpy(cdimgTOC->TrackData[i].EndAddress, pAddress, 4); + } + return 0; + } + + cdimgTOC->ImageType = nType; + cdimgTOC->FirstTrack = 1; + cdimgTOC->LastTrack = nTracks; + + cd_pregap = 150; + for (INT32 i = 0; i < nTracks; i++) { + const CDImageTrack* pTrack = CDImageGetTrack(cdimgImage, i); + cdimgTOC->TrackData[i].Control = (UINT8)pTrack->nControl; + cdimgTOC->TrackData[i].TrackNumber = tobcd(i + 1); + const UINT8* pAddress = cdimgLBAToMSF(pTrack->nStartLBA + cd_pregap); + memcpy(cdimgTOC->TrackData[i].Address, pAddress, 4); + INT32 nEnd = i + 1 < nTracks ? CDImageGetTrack(cdimgImage, i + 1)->nIndex0LBA : CDImageGetSectorCount(cdimgImage); + pAddress = cdimgLBAToMSF(nEnd + cd_pregap); + memcpy(cdimgTOC->TrackData[i].EndAddress, pAddress, 4); + } + + const UINT8* pLeadout = cdimgLBAToMSF(CDImageGetSectorCount(cdimgImage) + cd_pregap); + cdimgTOC->TrackData[nTracks].Control = 0x41; + cdimgTOC->TrackData[nTracks].TrackNumber = 0xaa; + memcpy(cdimgTOC->TrackData[nTracks].Address, pLeadout, 4); + const CDImageTrack* pFirst = CDImageGetTrack(cdimgImage, 0); + return cdimgCopyPath(cdimgTOC->Image, MAX_PATH, pFirst->szPath); +} + static INT32 cdimgParseChdFile() { cdimgChd = ChdOpenFile(CDEmuImage); @@ -636,7 +1272,7 @@ static INT32 cdimgParseChdFile() // Lead-out entry, just past the last valid sector. const UINT8* leadout = cdimgLBAToMSF(nTotal + cd_pregap); cdimgTOC->TrackData[nTracks].Control = 0x41; - cdimgTOC->TrackData[nTracks].TrackNumber = 0xAA; + cdimgTOC->TrackData[nTracks].TrackNumber = 0xaa; cdimgTOC->TrackData[nTracks].Address[0] = 0; cdimgTOC->TrackData[nTracks].Address[1] = leadout[1]; cdimgTOC->TrackData[nTracks].Address[2] = leadout[2]; @@ -672,32 +1308,12 @@ static INT32 cdimgInit() return 1; } - if (IsFileExt(filename, _T(".cue"))) { - if (cdimgParseCueFile()) { - dprintf(_T("*** Couldn't parse .cue file\n")); + if (IsFileExt(filename, _T(".cue")) || IsFileExt(filename, _T(".ccd")) || IsFileExt(filename, _T(".chd"))) { + if (cdimgParseImage()) { + dprintf(_T("*** Couldn't parse CD image\n")); cdimgExit(); - return 1; } - - } else - if (IsFileExt(filename, _T(".ccd"))) { - if (cdimgParseSubFile()) { - dprintf(_T("*** Couldn't parse .sub file\n")); - cdimgExit(); - - return 1; - } - - } else - if (IsFileExt(filename, _T(".chd"))) { - if (cdimgParseChdFile()) { - dprintf(_T("*** Couldn't parse .chd file\n")); - cdimgExit(); - - return 1; - } - } else { dprintf(_T("*** Couldn't find .img / .bin file\n")); cdimgExit(); @@ -726,7 +1342,7 @@ static INT32 cdimgInit() // Validate the CD by scanning the ISO-9660 volume descriptor at sector 16. // Both .bin and .chd images expose sector 16 as the first usable data sector. - if (cdimgTOC->ImageType == CD_TYPE_CHD) { + if (cdimgImage != NULL) { // Unified sector reader gives us a complete 2352-byte raw sector. // CD001 identifier lives at byte 1 of the 2048-byte user data area, // which is byte 17 (16 + 1) of the full 2352-byte sector. @@ -738,7 +1354,6 @@ static INT32 cdimgInit() } } else { FILE* h = _tfopen(cdimgTOC->Image, _T("rb")); - if (h) { if (cdimgFseek(h, 16 * 2352 + 16, SEEK_SET) == 0) { if (fread(buf, 1, 2048, h) == 2048) { @@ -750,7 +1365,6 @@ static INT32 cdimgInit() dprintf(_T("*** Bad CD!\n")); } } - fclose(h); } } @@ -774,7 +1388,6 @@ static INT32 cdimgStop() { cdimgCloseFile(); CDEmuStatus = idle; - return 0; } @@ -784,22 +1397,23 @@ static INT32 cdimgFindTrack(INT32 LBA) for (trk = cdimgTOC->FirstTrack - 1; trk < cdimgTOC->LastTrack; trk++) if (LBA < cdimgMSFToLBA(cdimgTOC->TrackData[trk + 1].Address)) break; + return trk; } -static INT32 cdimgPlayLBA(INT32 LBA) // audio play start +static INT32 cdimgPlayLBA(INT32 LBA) // audio play start { cdimgStop(); - if (QChannel != NULL) { // .CCD dump w/.SUB + if (QChannel != NULL) { // .CCD dump w/.SUB if (LBA < 0 || LBA >= QChannelCount || (QChannel[LBA].Control & 0x40)) return 1; - } else { // .BIN/.CUE dump or .CHD + } else { // .BIN/.CUE dump or .CHD if (cdimgTOC->TrackData[cdimgFindTrack(LBA)].Control & 0x40) return 1; } - cdimgLBA = LBA; + cdimgLBA = LBA; cdimgTrack = cdimgFindTrack(cdimgLBA); @@ -816,33 +1430,38 @@ static INT32 cdimgPlayLBA(INT32 LBA) // audio play start INT32 sectors_to_read = (cdimgOUT_SIZE * 4) / 2352; INT32 base; INT32 trackEnd = cdimgMSFToLBA(cdimgTOC->TrackData[cdimgTrack + 1].Address); - if (sectors_to_read > trackEnd - cdimgLBA) sectors_to_read = trackEnd - cdimgLBA; - if (sectors_to_read <= 0) return 1; + if (sectors_to_read > trackEnd - cdimgLBA) + sectors_to_read = trackEnd - cdimgLBA; + if (sectors_to_read <= 0) + return 1; // Logical LBA space is shared by CHD and .bin/.cue: subtract the disc // pregap. For CHD, cd_chd maps the logical LBA to its CHD frame per track. base = cdimgLBA - cd_pregap; - if (base < 0) base = 0; + if (base < 0) + base = 0; cdimgAudioFilePos = base; - if (cdimgTOC->ImageType == CD_TYPE_CHD) { - + if (cdimgImage != NULL) { UINT8 sector_buf[2352]; INT32 read_count = 0; for (INT32 i = 0; i < sectors_to_read; i++) { if (cdimgReadRawSector(base + i, sector_buf, true) != 0) break; - // Convert big-endian CD-DA data to native byte order - for (INT32 j = 0; j < 2352; j += 4) { - INT32 dst_idx = (i * 2352 + j) / 2; - cdimgOutputbuffer[dst_idx] = (sector_buf[j] << 8) | sector_buf[j + 1]; - cdimgOutputbuffer[dst_idx + 1] = (sector_buf[j + 2] << 8) | sector_buf[j + 3]; + if (cdimgTOC->ImageType == CD_TYPE_CHD) { + for (INT32 j = 0; j < 2352; j += 4) { + INT32 dst_idx = (i * 2352 + j) / 2; + cdimgOutputbuffer[dst_idx + 0] = (sector_buf[j + 0] << 8) | sector_buf[j + 1]; + cdimgOutputbuffer[dst_idx + 1] = (sector_buf[j + 2] << 8) | sector_buf[j + 3]; + } + } else { + memcpy((UINT8*)cdimgOutputbuffer + i * 2352, sector_buf, 2352); } read_count++; } - cdimgAudioFilePos = base + read_count; + cdimgAudioFilePos = base + read_count; cdimgOutputbufferSize = read_count * (2352 / 4); if (read_count == 0) { cdimgStop(); @@ -850,7 +1469,6 @@ static INT32 cdimgPlayLBA(INT32 LBA) // audio play start } } else { - cdimgFile = _tfopen(cdimgTOC->Image, _T("rb")); if (cdimgFile == NULL) return 1; @@ -863,17 +1481,17 @@ static INT32 cdimgPlayLBA(INT32 LBA) // audio play start // fill the input buffer (size returned as count of 4-byte sample units) cdimgOutputbufferSize = (INT32)fread(cdimgOutputbuffer, 4, sectors_to_read * (2352 / 4), cdimgFile); - cdimgAudioFilePos = (INT32)(cdimgFtell(cdimgFile) / 2352); + cdimgAudioFilePos = (INT32)(cdimgFtell(cdimgFile) / 2352); if (cdimgOutputbufferSize <= 0) { cdimgStop(); return 1; } } cdimgOutputPosition = 0; - cdimgSamples = 0; + cdimgSamples = 0; // this breaks states, commenting for now just in-case. -dink //cdimgLBA = cdimgMSFToLBA(cdimgTOC->TrackData[cdimgTrack].Address); // start at the beginning of track - CDEmuStatus = playing; + CDEmuStatus = playing; return 0; } @@ -890,7 +1508,8 @@ static INT32 cdimgPlay(UINT8 M, UINT8 S, UINT8 F) static INT32 cdimgLoadSector(INT32 LBA, char* pBuffer) { - if (CDEmuStatus == playing) return 0; // data loading + if (CDEmuStatus == playing) + return 0; // data loading INT32 originalLBA = LBA; if (CDEmuStatus == seeking) { @@ -901,7 +1520,7 @@ static INT32 cdimgLoadSector(INT32 LBA, char* pBuffer) // Unified sector reader: cdimgReadRawSector handles both .bin and .chd // transparently. For .bin files we still prefer the sequential-access // optimization below (avoids redundant fseek calls). - if (cdimgTOC && cdimgTOC->ImageType == CD_TYPE_CHD) { + if (cdimgTOC && cdimgImage != NULL) { if (cdimgReadRawSector(LBA, (UINT8*)pBuffer) != 0) { dprintf(_T("*** couldn't read sector (LBA %08u)\n"), originalLBA); return 0; @@ -911,7 +1530,7 @@ static INT32 cdimgLoadSector(INT32 LBA, char* pBuffer) // index of the next sector. cdimgTrack is kept in sync using a // full-disc LBA (with pregap) so cdimgFindTrack returns the correct // track index for the BIOS standby-screen Q-channel query. - cdimgLBA = LBA + 1; + cdimgLBA = LBA + 1; cdimgTrack = cdimgFindTrack(LBA + cd_pregap); return cdimgLBA; } @@ -967,7 +1586,6 @@ static INT32 cdimgLoadSector(INT32 LBA, char* pBuffer) static UINT8* cdimgReadTOC(INT32 track) { static UINT8 TOCEntry[4]; - memset(&TOCEntry, 0, sizeof(TOCEntry)); if (track == CDEmuTOC_FIRSTLAST) { @@ -982,7 +1600,6 @@ static UINT8* cdimgReadTOC(INT32 track) TOCEntry[0] = cdimgTOC->TrackData[cdimgTOC->LastTrack].Address[1]; TOCEntry[1] = cdimgTOC->TrackData[cdimgTOC->LastTrack].Address[2]; TOCEntry[2] = cdimgTOC->TrackData[cdimgTOC->LastTrack].Address[3]; - TOCEntry[3] = 0; return TOCEntry; @@ -1032,7 +1649,7 @@ static UINT8* cdimgReadQChannel() switch (CDEmuStatus) { case reading: case playing: { - if (QChannel != NULL && cdimgLBA >= 0 && cdimgLBA < QChannelCount) { // .CCD/.SUB + if (QChannel != NULL && cdimgLBA >= 0 && cdimgLBA < QChannelCount) { // .CCD/.SUB QChannelData[0] = QChannel[cdimgLBA].track; QChannelData[1] = QChannel[cdimgLBA].MSFabs.M; @@ -1044,7 +1661,7 @@ static UINT8* cdimgReadQChannel() QChannelData[6] = QChannel[cdimgLBA].MSFrel.F; QChannelData[7] = QChannel[cdimgLBA].Control; - } else { // .BIN/.ISO / .CHD + } else { // .BIN/.ISO / .CHD // cdimgLBA is the file-relative sector index for .cue // (data-track origin). For .chd we additionally keep // cdimgTrack set by cdimgFindTrack to identify which contains @@ -1084,7 +1701,6 @@ static UINT8* cdimgReadQChannel() static int cdimgSetVolume(double dVolume) { cd_volume = dVolume; - return 0; } @@ -1112,22 +1728,27 @@ static INT32 cdimgGetSoundBuffer(INT16* buffer, INT32 samples) if (cdimgOutputPosition >= cdimgOutputbufferSize) { cdimgOutputPosition = 0; INT32 remainingSectors = trackEnd - (cdimgAudioFilePos + cd_pregap); - INT32 sectorsToRead = (cdimgOUT_SIZE * 4) / 2352; + INT32 sectorsToRead = (cdimgOUT_SIZE * 4) / 2352; if (sectorsToRead > remainingSectors) sectorsToRead = remainingSectors; if (sectorsToRead <= 0) { cdimgStop(); break; } - if (cdimgTOC->ImageType == CD_TYPE_CHD) { + if (cdimgImage != NULL) { UINT8 sectorBuffer[2352]; INT32 readCount = 0; for (INT32 i = 0; i < sectorsToRead; i++) { - if (cdimgReadRawSector(cdimgAudioFilePos + i, sectorBuffer, true) != 0) break; - for (INT32 j = 0; j < 2352; j += 4) { - INT32 offset = (i * 2352 + j) / 2; - cdimgOutputbuffer[offset] = (sectorBuffer[j] << 8) | sectorBuffer[j + 1]; - cdimgOutputbuffer[offset + 1] = (sectorBuffer[j + 2] << 8) | sectorBuffer[j + 3]; + if (cdimgReadRawSector(cdimgAudioFilePos + i, sectorBuffer, true) != 0) + break; + if (cdimgTOC->ImageType == CD_TYPE_CHD) { + for (INT32 j = 0; j < 2352; j += 4) { + INT32 offset = (i * 2352 + j) / 2; + cdimgOutputbuffer[offset + 0] = (sectorBuffer[j + 0] << 8) | sectorBuffer[j + 1]; + cdimgOutputbuffer[offset + 1] = (sectorBuffer[j + 2] << 8) | sectorBuffer[j + 3]; + } + } else { + memcpy((UINT8*)cdimgOutputbuffer + i * 2352, sectorBuffer, 2352); } readCount++; } @@ -1151,17 +1772,19 @@ static INT32 cdimgGetSoundBuffer(INT16* buffer, INT32 samples) } INT32 count = cdimgOutputbufferSize - cdimgOutputPosition; - if (count > samples) count = samples; - if (count > remainingSamples) count = remainingSamples; + if (count > samples) + count = samples; + if (count > remainingSamples) + count = remainingSamples; INT16* src = cdimgOutputbuffer + cdimgOutputPosition * 2; for (INT32 i = count * 2 - 1; i >= 0; i--) { buffer[i] = CLIP((src[i] * (cd_volume / 100.0)) + buffer[i]); } - buffer += count * 2; + buffer += count * 2; cdimgOutputPosition += count; - samples -= count; - cdimgSamples += count; + samples -= count; + cdimgSamples += count; while (cdimgSamples >= (44100 / CD_FRAMES_SECOND)) { cdimgSamples -= 44100 / CD_FRAMES_SECOND; cdimgLBA++; diff --git a/src/intf/cd/cd_img.h b/src/intf/cd/cd_img.h index 072bed8ee..b1065be7d 100644 --- a/src/intf/cd/cd_img.h +++ b/src/intf/cd/cd_img.h @@ -1,6 +1,45 @@ #ifndef CD_IMG_H_ #define CD_IMG_H_ +#define CDIMAGE_MAX_TRACKS (99) +#define CDIMAGE_SHA1_SIZE (20) + +enum { + CDIMAGE_TRACK_AUDIO = 0, + CDIMAGE_TRACK_MODE1_2048, + CDIMAGE_TRACK_MODE1_2352, + CDIMAGE_TRACK_MODE2_2336, + CDIMAGE_TRACK_MODE2_2352 +}; + +struct CDImageTrack { + INT32 nNumber; + INT32 nType; + INT32 nControl; + INT32 nStartLBA; + INT32 nIndex0LBA; + INT32 nIndex1LBA; + INT32 nPregap; + INT32 nSectors; + INT32 nSectorSize; + INT32 nUserOffset; + INT32 nUserSize; + const TCHAR* szPath; +}; + +struct CDImage; + +CDImage* CDImageOpen(const TCHAR* szPath); +void CDImageClose(CDImage* pImage); +INT32 CDImageGetTrackCount(const CDImage* pImage); +INT32 CDImageGetAudioTrackCount(const CDImage* pImage); +INT32 CDImageGetSectorCount(const CDImage* pImage); +const TCHAR* CDImageGetPath(const CDImage* pImage); +const CDImageTrack* CDImageGetTrack(const CDImage* pImage, INT32 nTrack); +INT32 CDImageReadRawSector(CDImage* pImage, INT32 nLba, UINT8* pDest); +INT32 CDImageReadUserSector(CDImage* pImage, INT32 nLba, UINT8* pDest, INT32* pnSize); +INT32 CDImageGetChdSha1(const CDImage* pImage, UINT8* pSha1); + INT32 cdimgCountChdAudioTracks(TCHAR* pszFile); extern struct CDEmuDo cdimgDo;