diff --git a/include/supportbase.h b/include/supportbase.h index 95a57ae023..26929e054e 100644 --- a/include/supportbase.h +++ b/include/supportbase.h @@ -52,6 +52,7 @@ void sbCreateFolders(const char *path, int createDiscImgFolders); // ISO9660 filesystem management functions. u32 sbGetISO9660MaxLBA(const char *path); +u32 sbGetMediaLsnCount(const char *path, u64 totalBytes); int sbProbeISO9660(const char *path, base_game_info_t *game, u32 layer1_offset); int sbProbeISO9660_64(const char *path, base_game_info_t *game, u32 layer1_offset); diff --git a/modules/iopcore/cdvdman/searchfile.c b/modules/iopcore/cdvdman/searchfile.c index e409e82afb..e03ce9f449 100644 --- a/modules/iopcore/cdvdman/searchfile.c +++ b/modules/iopcore/cdvdman/searchfile.c @@ -263,4 +263,13 @@ void cdvdman_searchfile_init(void) DPRINTF("cdvdman_searchfile_init DVD9 mediaLsnCount=%d\n", mediaLsnCount); } } + + // The PVD Volume Space Size only describes the ISO9660 volume, not the whole disc. + // Badly mastered discs (e.g. SLES_533.98) keep data past the end of the volume and + // read it by raw LBA, which works on real hardware. Prefer the real media sector + // count measured by the loader whenever it was provided. + if (cdvdman_settings.common.mediaLsnCount) { + mediaLsnCount = cdvdman_settings.common.mediaLsnCount; + DPRINTF("cdvdman_searchfile_init loader-provided mediaLsnCount=%d\n", mediaLsnCount); + } } diff --git a/modules/iopcore/common/cdvd_config.h b/modules/iopcore/common/cdvd_config.h index 237d7b2783..5e9ef3f506 100644 --- a/modules/iopcore/common/cdvd_config.h +++ b/modules/iopcore/common/cdvd_config.h @@ -33,6 +33,7 @@ struct cdvdman_settings_common u8 zso_cache; u8 fakemodule_flags; u8 padding; + u32 mediaLsnCount; // Real sector count of the media. 0 = fall back to the ISO9660 PVD Volume Space Size. } __attribute__((packed)); struct cdvdman_settings_hdd @@ -88,9 +89,9 @@ struct cdvdman_settings_bdm bd_fragment_t frags[BDM_MAX_FRAGS]; } __attribute__((packed)); -#define CDVDMAN_SETTINGS_DEFAULT_COMMON \ - { \ - 0x68, 0x68, 0x1234, 0x39393939, "DSKID", 16, 8, 16 \ +#define CDVDMAN_SETTINGS_DEFAULT_COMMON \ + { \ + 0x68, 0x68, 0x1234, 0x39393939, "DSKID", 16, 8, 16, 0x87654321 \ } #define CDVDMAN_SETTINGS_DEFAULT_HDD 0x12345678 #define CDVDMAN_SETTINGS_DEFAULT_SMB \ diff --git a/src/bdmsupport.c b/src/bdmsupport.c index b181dec6a0..fac694d032 100644 --- a/src/bdmsupport.c +++ b/src/bdmsupport.c @@ -318,6 +318,7 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet) int i, fd, iop_fd, index, compatmask = 0; int EnablePS2Logo = 0; int result; + u64 isoTotalBytes = 0; u64 startingLBA; unsigned int startCluster; char partname[256], filename[32]; @@ -459,6 +460,8 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet) iso_frag->frag_count += iFragCount; iTotalFragCount += iFragCount; + isoTotalBytes += lseek64(fd, 0, SEEK_END); + if ((gPS2Logo) && (i == 0)) EnablePS2Logo = CheckPS2Logo(fd, 0); @@ -469,6 +472,11 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet) sbCreatePath(game, partname, pDeviceData->bdmPrefix, "/", 0); layer1_start = sbGetISO9660MaxLBA(partname); + // Real media size, for CDVDMAN's out-of-bounds read emulation. The ISO9660 PVD + // cannot be trusted for this: badly mastered discs understate it and read data + // past the end of the volume by raw LBA. + settings->common.mediaLsnCount = sbGetMediaLsnCount(partname, isoTotalBytes); + switch (game->format) { case GAME_FORMAT_USBLD: layer1_part = layer1_start / 0x80000; diff --git a/src/ethsupport.c b/src/ethsupport.c index f0462fa42a..3fbd3f8904 100644 --- a/src/ethsupport.c +++ b/src/ethsupport.c @@ -15,6 +15,7 @@ #define NEWLIB_PORT_AWARE #include // fileXioDevctl(ethBase, SMB_***) +#include // lseek64 #include "include/nbns.h" #include "httpclient.h" @@ -668,6 +669,18 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet strcpy(settings->smb_user, gPCUserName); strcpy(settings->smb_password, gPCPassword); + // Sum the size of all parts, for CDVDMAN's out-of-bounds read emulation. + u64 isoTotalBytes = 0; + int part; + for (part = 0; part < game->parts; part++) { + sbCreatePath(game, partname, ethPrefix, "\\", part); + int fd = open(partname, O_RDONLY, 0666); + if (fd >= 0) { + isoTotalBytes += lseek64(fd, 0, SEEK_END); + close(fd); + } + } + // Initialize layer 1 information. sbCreatePath(game, partname, ethPrefix, "\\", 0); @@ -681,6 +694,10 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet layer1_start = sbGetISO9660MaxLBA(partname); + // Real media size; the ISO9660 PVD cannot be trusted for this (badly mastered + // discs understate it and read data past the end of the volume by raw LBA). + settings->common.mediaLsnCount = sbGetMediaLsnCount(partname, isoTotalBytes); + switch (game->format) { case GAME_FORMAT_USBLD: layer1_part = layer1_start / 0x80000; diff --git a/src/hddsupport.c b/src/hddsupport.c index 1f3eaefa87..5aa330e019 100644 --- a/src/hddsupport.c +++ b/src/hddsupport.c @@ -578,6 +578,11 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet) // patch start_sector settings->lba_start = game->start_sector; + // Real media size, for CDVDMAN's out-of-bounds read emulation. The ISO9660 PVD + // cannot be trusted for this: badly mastered discs understate it and read data + // past the end of the volume by raw LBA. + settings->common.mediaLsnCount = game->total_size_in_kb / 2; + if (configGetStrCopy(configSet, CONFIG_ITEM_ALTSTARTUP, filename, sizeof(filename)) == 0) strcpy(filename, game->startup); @@ -596,6 +601,8 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet) if (maxLBA > 0 && maxLBA < ziso_total_block) { // dual layer check settings->common.layer1_start = maxLBA - 16; // adjust second layer start } + // For compressed images the partition size does not match the media size. + settings->common.mediaLsnCount = ziso_total_block; } if (gAutoLaunchGame == NULL) diff --git a/src/supportbase.c b/src/supportbase.c index 63cd6ad2e4..0a1f988efa 100644 --- a/src/supportbase.c +++ b/src/supportbase.c @@ -518,6 +518,26 @@ u32 sbGetISO9660MaxLBA(const char *path) return maxLBA; } +u32 sbGetMediaLsnCount(const char *path, u64 totalBytes) +{ + u32 lsnCount; + int fd; + + // For compressed images the file size does not match the media size, so use + // the uncompressed sector count from the ZISO header instead. + lsnCount = 0; + if ((fd = open(path, O_RDONLY, 0666)) >= 0) { + if (ProbeZISO(fd)) + lsnCount = ziso_total_block; + close(fd); + } + + if (lsnCount == 0) + lsnCount = (u32)(totalBytes / 2048); + + return lsnCount; +} + int sbProbeISO9660(const char *path, base_game_info_t *game, u32 layer1_offset) { int result = -1, fd; @@ -576,6 +596,7 @@ int sbPrepare(base_game_info_t *game, config_set_t *configSet, int size_cdvdman, settings->media = game->media; } settings->flags = 0; + settings->mediaLsnCount = 0; if (compatmask & COMPAT_MODE_1) { settings->flags |= IOPCORE_COMPAT_ACCU_READS;