mirror of
https://github.com/GerbilSoft/rom-properties.git
synced 2025-06-18 11:35:38 -04:00
[libromdata] CisoPspReader: Added initial support for JISO.
It has a unique header, but the index entries table is similar to the other formats. It does *not* set the high bit to indicate NC; instead, it uses the same method as CISOv2, where the compressed block size matches the uncompressed block size. Compression algorithm is lzo1x. TODO: - JISO supports NC areas. Figure this part out. - Add LZO to extlib.
This commit is contained in:
parent
85485f9d37
commit
640cb2bf8a
@ -125,6 +125,7 @@ INCLUDE(CheckJPEG)
|
||||
INCLUDE(CheckTinyXML2)
|
||||
INCLUDE(CheckZSTD)
|
||||
INCLUDE(CheckLZ4)
|
||||
INCLUDE(CheckLZO)
|
||||
|
||||
# Reference: https://cmake.org/Wiki/RecipeAddUninstallTarget
|
||||
########### Add uninstall target ###############
|
||||
@ -220,6 +221,16 @@ ELSE(ENABLE_LZ4)
|
||||
SET(ENABLE_LZ4_MSG "Disabled")
|
||||
ENDIF(ENABLE_LZ4)
|
||||
|
||||
IF(ENABLE_LZO)
|
||||
IF(USE_INTERNAL_LZO)
|
||||
SET(ENABLE_LZO_MSG "Enabled (bundled)")
|
||||
ELSE(USE_INTERNAL_LZO)
|
||||
SET(ENABLE_LZO_MSG "Enabled (system)")
|
||||
ENDIF(USE_INTERNAL_LZO)
|
||||
ELSE(ENABLE_LZO)
|
||||
SET(ENABLE_LZO_MSG "Disabled")
|
||||
ENDIF(ENABLE_LZO)
|
||||
|
||||
|
||||
UNSET(EXTLIB_BUILD)
|
||||
IF(USE_INTERNAL_ZLIB)
|
||||
@ -237,6 +248,9 @@ ENDIF(USE_INTERNAL_ZSTD)
|
||||
IF(USE_INTERNAL_LZ4)
|
||||
SET(EXTLIB_BUILD "${EXTLIB_BUILD} - lz4\n")
|
||||
ENDIF(USE_INTERNAL_LZ4)
|
||||
IF(USE_INTERNAL_LZO)
|
||||
SET(EXTLIB_BUILD "${EXTLIB_BUILD} - lzo\n")
|
||||
ENDIF(USE_INTERNAL_LZO)
|
||||
SET(EXTLIB_BUILD "${EXTLIB_BUILD} - minizip\n")
|
||||
SET(EXTLIB_BUILD "${EXTLIB_BUILD} - inih\n")
|
||||
IF(USE_INTERNAL_XML)
|
||||
@ -258,6 +272,7 @@ MESSAGE("
|
||||
- PVRTC decoder: ${ENABLE_PVRTC_MSG}
|
||||
- ZSTD decompression: ${ENABLE_ZSTD_MSG}
|
||||
- LZ4 decompression: ${ENABLE_LZ4_MSG}
|
||||
- LZO decompression: ${ENABLE_LZO_MSG}
|
||||
|
||||
- Building these third-party libraries from extlib:
|
||||
${EXTLIB_BUILD}")
|
||||
|
53
cmake/libs/CheckLZO.cmake
Normal file
53
cmake/libs/CheckLZO.cmake
Normal file
@ -0,0 +1,53 @@
|
||||
# Check for LZO.
|
||||
# If LZO isn't found, extlib/lzo/ will be used instead.
|
||||
|
||||
IF(ENABLE_LZO)
|
||||
|
||||
IF(NOT USE_INTERNAL_LZO)
|
||||
IF(LZO_LIBRARY MATCHES "^lzo$" OR LZO_LIBRARY MATCHES "^lzo")
|
||||
# Internal LZO was previously in use.
|
||||
UNSET(LZO_FOUND)
|
||||
UNSET(HAVE_LZO)
|
||||
UNSET(LZO_LIBRARY CACHE)
|
||||
UNSET(LZO_LIBRARIES CACHE)
|
||||
ENDIF()
|
||||
|
||||
# Check for LZO.
|
||||
FIND_PACKAGE(LZO)
|
||||
IF(LZO_FOUND)
|
||||
# Found system LZO.
|
||||
SET(HAVE_LZO 1)
|
||||
ELSE()
|
||||
# System LZO was not found.
|
||||
MESSAGE(STATUS "Using the internal copy of LZO since a system version was not found.")
|
||||
# FIXME: Uncomment after adding extlib/lzo/.
|
||||
#SET(USE_INTERNAL_LZO ON CACHE BOOL "Use the internal copy of LZO" FORCE)
|
||||
SET(ENABLE_LZO OFF CACHE BOOL "Enable LZO decompression. (Required for some PSP disc formats.)" FORCE)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
MESSAGE(STATUS "Using the internal copy of LZO.")
|
||||
ENDIF(NOT USE_INTERNAL_LZO)
|
||||
|
||||
IF(USE_INTERNAL_LZO)
|
||||
# Using the internal LZO library.
|
||||
SET(LZO_FOUND 1)
|
||||
SET(HAVE_LZO 1)
|
||||
# FIXME: When was it changed from LIBRARY to LIBRARIES?
|
||||
IF(WIN32 OR APPLE)
|
||||
# Using DLLs on Windows and Mac OS X.
|
||||
SET(USE_INTERNAL_LZO_DLL ON)
|
||||
SET(LZO_LIBRARY lzo_shared CACHE INTERNAL "LZO library" FORCE)
|
||||
ELSE()
|
||||
# Using static linking on other systems.
|
||||
SET(USE_INTERNAL_LZO_DLL OFF)
|
||||
SET(LZO_LIBRARY lzo_static CACHE INTERNAL "LZO library" FORCE)
|
||||
ENDIF()
|
||||
SET(LZO_LIBRARIES ${LZO_LIBRARY})
|
||||
# FIXME: When was it changed from DIR to DIRS?
|
||||
SET(LZO_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/extlib/lzo)
|
||||
SET(LZO_INCLUDE_DIRS ${LZO_INCLUDE_DIR})
|
||||
ELSE(USE_INTERNAL_LZO)
|
||||
SET(USE_INTERNAL_LZO_DLL OFF)
|
||||
ENDIF(USE_INTERNAL_LZO)
|
||||
|
||||
ENDIF(ENABLE_LZO)
|
22
cmake/libs/FindLZO.cmake
Normal file
22
cmake/libs/FindLZO.cmake
Normal file
@ -0,0 +1,22 @@
|
||||
# Find LZO libraries and headers.
|
||||
# If found, the following variables will be defined:
|
||||
# - LZO_FOUND: System has LZO.
|
||||
# - LZO_INCLUDE_DIRS: LZO include directories.
|
||||
# - LZO_LIBRARIES: LZO libraries.
|
||||
# - LZO_DEFINITIONS: Compiler switches required for using LZO.
|
||||
#
|
||||
# In addition, a target LZO::lzo will be created with all of
|
||||
# these definitions.
|
||||
#
|
||||
# References:
|
||||
# - https://cmake.org/Wiki/CMake:How_To_Find_Libraries
|
||||
# - http://francesco-cek.com/cmake-and-gtk-3-the-easy-way/
|
||||
#
|
||||
|
||||
INCLUDE(FindLibraryPkgConfig)
|
||||
FIND_LIBRARY_PKG_CONFIG(LZO
|
||||
lzo2 # pkgconfig
|
||||
lzo/lzo1x.h # header
|
||||
lzo2 # library
|
||||
LZO::lzo # imported target
|
||||
)
|
@ -45,6 +45,9 @@ OPTION(BUILD_CLI "Build the `rpcli` command line program." ON)
|
||||
OPTION(ENABLE_XML "Enable XML parsing for e.g. Windows manifests." ON)
|
||||
OPTION(ENABLE_ZSTD "Enable ZSTD decompression. (Required for some unit tests.)" ON)
|
||||
OPTION(ENABLE_LZ4 "Enable LZ4 decompression. (Required for some PSP disc formats.)" ON)
|
||||
IF(NOT WIN32)
|
||||
OPTION(ENABLE_LZO "Enable LZO decompression. (Required for some PSP disc formats.)" ON)
|
||||
ENDIF(NOT WIN32)
|
||||
|
||||
IF(WIN32)
|
||||
SET(USE_INTERNAL_ZLIB ON)
|
||||
@ -52,12 +55,14 @@ IF(WIN32)
|
||||
SET(USE_INTERNAL_XML ${ENABLE_XML})
|
||||
SET(USE_INTERNAL_ZSTD ${ENABLE_ZSTD})
|
||||
SET(USE_INTERNAL_LZ4 ${ENABLE_LZ4})
|
||||
SET(USE_INTERNAL_LZO ${ENABLE_LZO})
|
||||
ELSE(WIN32)
|
||||
OPTION(USE_INTERNAL_ZLIB "Use the internal copy of zlib." OFF)
|
||||
OPTION(USE_INTERNAL_PNG "Use the internal copy of libpng." OFF)
|
||||
OPTION(USE_INTERNAL_XML "Use the internal copy of TinyXML2." OFF)
|
||||
OPTION(USE_INTERNAL_ZSTD "Use the internal copy of zstd." OFF)
|
||||
OPTION(USE_INTERNAL_LZ4 "Use the internal copy of LZ4." OFF)
|
||||
OPTION(USE_INTERNAL_LZO "Use the internal copy of LZO." OFF)
|
||||
ENDIF()
|
||||
|
||||
# TODO: If APNG export is added, verify that system libpng
|
||||
|
@ -455,6 +455,9 @@ ENDIF(ENABLE_LIBMSPACK)
|
||||
IF(ENABLE_LZ4 AND LZ4_FOUND)
|
||||
TARGET_LINK_LIBRARIES(romdata PRIVATE ${LZ4_LIBRARY})
|
||||
ENDIF(ENABLE_LZ4 AND LZ4_FOUND)
|
||||
IF(ENABLE_LZO AND LZO_FOUND)
|
||||
TARGET_LINK_LIBRARIES(romdata PRIVATE ${LZO_LIBRARY})
|
||||
ENDIF(ENABLE_LZO AND LZO_FOUND)
|
||||
|
||||
# Unix: Add -fpic/-fPIC in order to use this static library in plugins.
|
||||
IF(UNIX AND NOT APPLE)
|
||||
|
@ -265,6 +265,9 @@ const RomDataFactoryPrivate::RomDataFns RomDataFactoryPrivate::romDataFns_magic[
|
||||
#ifdef HAVE_LZ4
|
||||
GetRomDataFns_addr(PSP, ATTR_HAS_THUMBNAIL | ATTR_HAS_METADATA, 0, 'ZISO'),
|
||||
#endif /* HAVE_LZ4 */
|
||||
#ifdef HAVE_LZO
|
||||
GetRomDataFns_addr(PSP, ATTR_HAS_THUMBNAIL | ATTR_HAS_METADATA, 0, 'JISO'),
|
||||
#endif /* HAVE_LZO */
|
||||
GetRomDataFns_addr(PSP, ATTR_HAS_THUMBNAIL | ATTR_HAS_METADATA, 0, 0x44415800), // 'DAX\0'
|
||||
|
||||
// Audio
|
||||
|
@ -30,9 +30,23 @@
|
||||
/* Define to 1 if we're using the internal copy of LZ4 as a DLL. */
|
||||
#cmakedefine USE_INTERNAL_LZ4_DLL 1
|
||||
|
||||
/* Define to 1 if zlib is a DLL. */
|
||||
/* Define to 1 if LZ4 is a DLL. */
|
||||
#if !defined(USE_INTERNAL_LZ4) || defined(USE_INTERNAL_LZ4_DLL)
|
||||
# define LZ4_IS_DLL 1
|
||||
#endif
|
||||
|
||||
/* Define to 1 if you have LZO. */
|
||||
#cmakedefine HAVE_LZO 1
|
||||
|
||||
/* Define to 1 if we're using the internal copy of LZO. */
|
||||
#cmakedefine USE_INTERNAL_LZO 1
|
||||
|
||||
/* Define to 1 if we're using the internal copy of LZO as a DLL. */
|
||||
#cmakedefine USE_INTERNAL_LZO_DLL 1
|
||||
|
||||
/* Define to 1 if LZO is a DLL. */
|
||||
#if !defined(USE_INTERNAL_LZO) || defined(USE_INTERNAL_LZO_DLL)
|
||||
# define LZO_IS_DLL 1
|
||||
#endif
|
||||
|
||||
#endif /* __ROMPROPERTIES_LIBROMDATA_CONFIG_H__ */
|
||||
|
@ -29,6 +29,11 @@
|
||||
# include <lz4.h>
|
||||
#endif /* HAVE_LZ4 */
|
||||
|
||||
// LZO (JISO)
|
||||
#ifdef HAVE_LZO
|
||||
# include <lzo/lzo1x.h>
|
||||
#endif /* HAVE_LZO */
|
||||
|
||||
// librpbase, librpfile
|
||||
using namespace LibRpBase;
|
||||
using LibRpFile::IRpFile;
|
||||
@ -44,6 +49,9 @@ DELAYLOAD_TEST_FUNCTION_IMPL0(zlibVersion);
|
||||
# ifdef HAVE_LZ4
|
||||
DELAYLOAD_TEST_FUNCTION_IMPL0(LZ4_versionNumber);
|
||||
# endif /* HAVE_LZ4 */
|
||||
# ifdef HAVE_LZO
|
||||
DELAYLOAD_TEST_FUNCTION_IMPL0(lzo_version);
|
||||
# endif /* HAVE_LZO */
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
class CisoPspReaderPrivate : public SparseDiscReaderPrivate {
|
||||
@ -58,11 +66,15 @@ class CisoPspReaderPrivate : public SparseDiscReaderPrivate {
|
||||
enum class CisoType {
|
||||
Unknown = -1,
|
||||
|
||||
CISO = 0, // CISO
|
||||
CISO = 0,
|
||||
#ifdef HAVE_LZ4
|
||||
ZISO = 1, // ZISO
|
||||
ZISO = 1,
|
||||
#endif /* HAVE_LZ4 */
|
||||
DAX = 2, // DAX
|
||||
#ifdef HAVE_LZO
|
||||
JISO = 2,
|
||||
#endif /* HAVE_LZO */
|
||||
|
||||
DAX = 3,
|
||||
|
||||
Max
|
||||
};
|
||||
@ -71,6 +83,9 @@ class CisoPspReaderPrivate : public SparseDiscReaderPrivate {
|
||||
// Header.
|
||||
union {
|
||||
CisoPspHeader cisoPsp;
|
||||
#ifdef HAVE_LZO
|
||||
JisoHeader jiso;
|
||||
#endif /* HAVE_LZO */
|
||||
DaxHeader dax;
|
||||
} header;
|
||||
|
||||
@ -154,6 +169,22 @@ uint32_t CisoPspReaderPrivate::getBlockCompressedSize(uint32_t blockNum) const
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef HAVE_LZO
|
||||
case CisoPspReaderPrivate::CisoType::JISO:
|
||||
// Index entry table has an extra entry for the final block.
|
||||
// Hence, we don't need the same workaround as GCZ.
|
||||
// NOTE: JISO doesn't use the high bit for NC.
|
||||
assert(blockNum != indexEntries.size()-1);
|
||||
if (blockNum < indexEntries.size()-1) {
|
||||
off64_t idxStart = le32_to_cpu(indexEntries[blockNum]);
|
||||
off64_t idxEnd = le32_to_cpu(indexEntries[blockNum + 1]);
|
||||
idxStart <<= index_shift;
|
||||
idxEnd <<= index_shift;
|
||||
size = static_cast<uint32_t>(idxEnd - idxStart);
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_LZO */
|
||||
|
||||
case CisoPspReaderPrivate::CisoType::DAX:
|
||||
// DAX uses a separate size table.
|
||||
size = static_cast<uint32_t>(daxSizeTable[blockNum]);
|
||||
@ -201,6 +232,9 @@ CisoPspReader::CisoPspReader(IRpFile *file)
|
||||
# if defined(HAVE_LZ4) && defined(LZ4_IS_DLL)
|
||||
bool isLZ4 = false;
|
||||
# endif /* HAVE_LZ4 && LZ4_IS_DLL */
|
||||
# if defined(HAVE_LZO) && defined(LZO_IS_DLL)
|
||||
bool isLZO = false;
|
||||
# endif /* HAVE_LZO && LZO_IS_DLL */
|
||||
#endif /* MSC_VER */
|
||||
switch (d->cisoType) {
|
||||
default:
|
||||
@ -241,6 +275,26 @@ CisoPspReader::CisoPspReader(IRpFile *file)
|
||||
#endif /* _MSC_VER && HAVE_LZ4 */
|
||||
break;
|
||||
|
||||
#ifdef HAVE_LZO
|
||||
case CisoPspReaderPrivate::CisoType::JISO:
|
||||
#if defined(_MSC_VER) && defined(LZO_IS_DLL)
|
||||
isLZO = true;
|
||||
#endif /* _MSC_VER && LZO_IS_DLL */
|
||||
#if SYS_BYTEORDER != SYS_LIL_ENDIAN
|
||||
// Byteswap the header.
|
||||
d->header.jiso.magic = le32_to_cpu(d->header.jiso.magic);
|
||||
d->header.jiso.block_size = le16_to_cpu(d->header.jiso.block_size);
|
||||
d->header.jiso.uncompressed_size = le32_to_cpu(d->header.jiso.uncompressed_size);
|
||||
d->header.jiso.header_size = le32_to_cpu(d->header.jiso.block_size);
|
||||
#endif /* SYS_BYTEORDER != SYS_LIL_ENDIAN */
|
||||
|
||||
d->block_size = d->header.jiso.block_size;
|
||||
d->disc_size = d->header.jiso.uncompressed_size;
|
||||
// TODO: index_shift field?
|
||||
indexEntryTblPos = static_cast<off64_t>(sizeof(d->header.jiso));
|
||||
break;
|
||||
#endif /* HAVE_LZO */
|
||||
|
||||
case CisoPspReaderPrivate::CisoType::DAX:
|
||||
#if defined(_MSC_VER) && defined(ZLIB_IS_DLL)
|
||||
isZlib = true;
|
||||
@ -281,6 +335,15 @@ CisoPspReader::CisoPspReader(IRpFile *file)
|
||||
}
|
||||
}
|
||||
# endif /* HAVE_LZ4 && LZ4_IS_DLL */
|
||||
# if defined(HAVE_LZO) && defined(LZO_IS_DLL)
|
||||
if (isLZO) {
|
||||
if (DelayLoad_test_lzo_version() != 0) {
|
||||
// Delay load for LZO failed.
|
||||
UNREF_AND_NULL_NOCHK(m_file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
# endif /* HAVE_LZ4 && LZ4_IS_DLL */
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
// Calculate the number of blocks.
|
||||
@ -295,13 +358,19 @@ CisoPspReader::CisoPspReader(IRpFile *file)
|
||||
// Read the index entries.
|
||||
// NOTE: These are byteswapped on demand, not ahead of time.
|
||||
uint32_t num_blocks_alloc = num_blocks;
|
||||
if (d->cisoType == CisoPspReaderPrivate::CisoType::CISO
|
||||
switch (d->cisoType) {
|
||||
case CisoPspReaderPrivate::CisoType::CISO:
|
||||
#ifdef HAVE_LZ4
|
||||
|| d->cisoType == CisoPspReaderPrivate::CisoType::ZISO
|
||||
case CisoPspReaderPrivate::CisoType::ZISO:
|
||||
#endif /* HAVE_LZ4 */
|
||||
)
|
||||
{
|
||||
num_blocks_alloc++;
|
||||
#ifdef HAVE_LZO
|
||||
case CisoPspReaderPrivate::CisoType::JISO:
|
||||
#endif /* HAVE_LZO */
|
||||
num_blocks_alloc++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
d->indexEntries.resize(num_blocks_alloc);
|
||||
size_t expected_size = num_blocks_alloc * sizeof(uint32_t);
|
||||
@ -316,6 +385,12 @@ CisoPspReader::CisoPspReader(IRpFile *file)
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->cisoType == CisoPspReaderPrivate::CisoType::JISO) {
|
||||
// Initialize LZO.
|
||||
lzo_init();
|
||||
}
|
||||
|
||||
// TODO: NC areas for JISO.
|
||||
if (d->cisoType == CisoPspReaderPrivate::CisoType::DAX) {
|
||||
// Read the DAX size table.
|
||||
d->daxSizeTable.resize(num_blocks);
|
||||
@ -420,7 +495,7 @@ int CisoPspReader::isDiscSupported_static(const uint8_t *pHeader, size_t szHeade
|
||||
#endif /* HAVE_LZ4 */
|
||||
) && szHeader >= sizeof(CisoPspHeader))
|
||||
{
|
||||
// CISO magic.
|
||||
// CISO/ZISO magic.
|
||||
const CisoPspHeader *const cisoPspHeader = reinterpret_cast<const CisoPspHeader*>(pHeader);
|
||||
|
||||
// Header size:
|
||||
@ -482,6 +557,39 @@ int CisoPspReader::isDiscSupported_static(const uint8_t *pHeader, size_t szHeade
|
||||
}
|
||||
#endif /* HAVE_LZ4 */
|
||||
return (int)CisoPspReaderPrivate::CisoType::CISO;
|
||||
} else if (*pu32 == be32_to_cpu(JISO_MAGIC) && szHeader >= sizeof(JisoHeader)) {
|
||||
// JISO magic.
|
||||
const JisoHeader *const jisoHeader = reinterpret_cast<const JisoHeader*>(pHeader);
|
||||
|
||||
// TODO: Version number field?
|
||||
|
||||
// Check if the block size is a supported power of two.
|
||||
// - Minimum: JISO_BLOCK_SIZE_MIN ( 2 KB, 1 << 11)
|
||||
// - Maximum: JISO_BLOCK_SIZE_MAX (64 KB, 1 << 16)
|
||||
const uint32_t block_size = le32_to_cpu(jisoHeader->block_size);
|
||||
if (!isPow2(block_size) ||
|
||||
block_size < JISO_BLOCK_SIZE_MIN || block_size > JISO_BLOCK_SIZE_MAX)
|
||||
{
|
||||
// Block size is out of range.
|
||||
return (int)CisoPspReaderPrivate::CisoType::Unknown;
|
||||
}
|
||||
|
||||
// Must have at least one block and less than 4 GB of uncompressed data.
|
||||
// NOTE: 4 GB is the implied maximum size due to use of uint32_t.
|
||||
const uint32_t uncompressed_size = le32_to_cpu(jisoHeader->uncompressed_size);
|
||||
if (uncompressed_size < DAX_BLOCK_SIZE) {
|
||||
// Less than one block...
|
||||
return (int)CisoPspReaderPrivate::CisoType::Unknown;
|
||||
}
|
||||
|
||||
// Uncompressed data size must be a multiple of the block size.
|
||||
if (uncompressed_size % block_size != 0) {
|
||||
// Not a multiple.
|
||||
return (int)CisoPspReaderPrivate::CisoType::Unknown;
|
||||
}
|
||||
|
||||
// This is a valid JISO image.
|
||||
return (int)CisoPspReaderPrivate::CisoType::JISO;
|
||||
} else if (*pu32 == be32_to_cpu(DAX_MAGIC) && szHeader >= sizeof(DaxHeader)) {
|
||||
// DAX magic.
|
||||
const DaxHeader *const daxHeader = reinterpret_cast<const DaxHeader*>(pHeader);
|
||||
@ -562,6 +670,14 @@ off64_t CisoPspReader::getPhysBlockAddr(uint32_t blockIdx) const
|
||||
addr <<= d->index_shift;
|
||||
break;
|
||||
|
||||
#ifdef HAVE_LZO
|
||||
case CisoPspReaderPrivate::CisoType::JISO:
|
||||
// TODO: Is index_shift actually supported by JISO?
|
||||
addr = static_cast<off64_t>(d->indexEntries[blockIdx]);
|
||||
addr <<= d->index_shift;
|
||||
break;
|
||||
#endif /* HAVE_LZO */
|
||||
|
||||
case CisoPspReaderPrivate::CisoType::DAX:
|
||||
addr = static_cast<off64_t>(d->indexEntries[blockIdx]);
|
||||
break;
|
||||
@ -621,6 +737,7 @@ int CisoPspReader::readBlock(uint32_t blockIdx, void *ptr, int pos, size_t size)
|
||||
None = 0,
|
||||
Deflate = 1,
|
||||
LZ4 = 2,
|
||||
LZO = 3,
|
||||
};
|
||||
CompressionMode z_mode;
|
||||
int windowBits = -15; // raw deflate (CISO)
|
||||
@ -684,6 +801,23 @@ int CisoPspReader::readBlock(uint32_t blockIdx, void *ptr, int pos, size_t size)
|
||||
break;
|
||||
#endif /* HAVE_LZ4 */
|
||||
|
||||
#ifdef HAVE_LZO
|
||||
case CisoPspReaderPrivate::CisoType::JISO:
|
||||
// JISO uses LZO.
|
||||
// TODO: Verify the rest of this.
|
||||
|
||||
// JISO does *not* indicate compression using the high bit.
|
||||
// Instead, the compressed block size will match the uncompressed
|
||||
// block size, similar to CISOv2.
|
||||
physBlockAddr = static_cast<off64_t>(indexEntry);
|
||||
physBlockAddr <<= d->index_shift;
|
||||
|
||||
z_mode = (z_block_size == d->block_size)
|
||||
? CompressionMode::None
|
||||
: CompressionMode::LZO;
|
||||
break;
|
||||
#endif /* HAVE_LZO */
|
||||
|
||||
case CisoPspReaderPrivate::CisoType::DAX:
|
||||
physBlockAddr = static_cast<off64_t>(indexEntry);
|
||||
if (d->header.dax.nc_areas > 0 && d->daxNCTable[blockIdx]) {
|
||||
@ -815,6 +949,55 @@ int CisoPspReader::readBlock(uint32_t blockIdx, void *ptr, int pos, size_t size)
|
||||
return 0;
|
||||
#endif /* HAVE_LZ4 */
|
||||
}
|
||||
|
||||
case CompressionMode::LZO: {
|
||||
#ifdef HAVE_LZO
|
||||
// Read compressed data into a temporary buffer,
|
||||
// then decompress it.
|
||||
uint32_t z_max_size = d->block_size;
|
||||
if (unlikely(d->isDaxWithoutNCTable)) {
|
||||
// DAX without NC table can end up compressing to larger
|
||||
// than the uncompressed size.
|
||||
z_max_size *= 2;
|
||||
}
|
||||
if (z_block_size > z_max_size) {
|
||||
// Compressed data is larger than the uncompressed block size.
|
||||
// This is only allowed for DAX without NC table.
|
||||
m_lastError = EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sz_read = m_file->seekAndRead(physBlockAddr, d->z_buffer.data(), z_block_size);
|
||||
if (sz_read != z_block_size) {
|
||||
// Seek and/or read error.
|
||||
m_lastError = m_file->lastError();
|
||||
if (m_lastError == 0) {
|
||||
m_lastError = EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Decompress the data.
|
||||
// TODO: LZO in-place decompression?
|
||||
lzo_uint dst_len = d->block_size;
|
||||
int ret = lzo1x_decompress_safe(
|
||||
d->z_buffer.data(), z_block_size,
|
||||
d->blockCache.data(), &dst_len,
|
||||
nullptr);
|
||||
if (ret != LZO_E_OK || dst_len != d->block_size) {
|
||||
// Decompression error.
|
||||
// TODO: Print warnings and/or more comprehensive error codes.
|
||||
d->blockCacheIdx = ~0U;
|
||||
m_lastError = EIO;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
#else /* !HAVE_LZO */
|
||||
assert(!"LZO is not enabled in this build.");
|
||||
m_lastError = EIO;
|
||||
return 0;
|
||||
#endif /* HAVE_LZO */
|
||||
}
|
||||
}
|
||||
|
||||
// Block has been loaded into the cache.
|
||||
|
@ -78,6 +78,39 @@ ASSERT_STRUCT(DaxNCArea, 2*sizeof(uint32_t));
|
||||
// DAX has a fixed block size.
|
||||
#define DAX_BLOCK_SIZE 0x2000
|
||||
|
||||
/**
|
||||
* PlayStation Portable JISO header.
|
||||
* NOTE: Based on reverse-engineered samples,
|
||||
* so this may be incomplete.
|
||||
*
|
||||
* - An extra index entry is included to determine the size of the
|
||||
* last compressed block, similar to CISO.
|
||||
* - Index entries do NOT use the high bit to indicate uncompressed.
|
||||
* - TODO: There might be an NC area.
|
||||
* - If a block is uncompressed, the difference between index entries
|
||||
* equals the block size. (Same as CISOv2.)
|
||||
*
|
||||
* All fields are in little-endian.
|
||||
*/
|
||||
#define JISO_MAGIC 'JISO' // JISO
|
||||
typedef struct _JisoHeader {
|
||||
uint32_t magic; // [0x000] 'JISO'
|
||||
uint8_t unk3; // [0x004] 0x03?
|
||||
uint8_t unk1; // [0x005] 0x01?
|
||||
uint16_t block_size; // [0x006] Block size, usually 2048.
|
||||
uint32_t unk_nc_area; // [0x008] Unknown (NC areas?)
|
||||
uint32_t uncompressed_size; // [0x00C] Uncompressed data size.
|
||||
uint8_t unknown_data[16]; // [0x010]
|
||||
uint32_t header_size; // [0x020] Header size? (0x30)
|
||||
uint8_t unknown[12]; // [0x024]
|
||||
} JisoHeader;
|
||||
ASSERT_STRUCT(JisoHeader, 0x30);
|
||||
|
||||
// 2 KB minimum block size (DVD sector)
|
||||
// 64 KB maximum block size
|
||||
#define JISO_BLOCK_SIZE_MIN (2048)
|
||||
#define JISO_BLOCK_SIZE_MAX (64*1024)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
@ -68,6 +68,7 @@ application/x-nintendo-ds-rom # NintendoDS
|
||||
application/x-nintendo-dsi-rom # NintendoDS
|
||||
application/x-psp-ciso-image # PSP
|
||||
application/x-psp-dax-image # PSP
|
||||
application/x-psp-jiso-image # PSP
|
||||
application/x-psp-ziso-image # PSP
|
||||
|
||||
# Other
|
||||
|
@ -573,6 +573,27 @@
|
||||
</match>
|
||||
</magic>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-psp-dax-image">
|
||||
<comment>PlayStation Portable DAX disc image</comment>
|
||||
<sub-class-of type="application/x-cd-image"/>
|
||||
<generic-icon name="application-x-cd-image"/>
|
||||
<glob pattern="*.dax"/>
|
||||
<magic priority="50">
|
||||
<match value="0x41445800" type="big32" offset="0"/>
|
||||
</magic>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-psp-jiso-image">
|
||||
<comment>PlayStation Portable JISO disc image</comment>
|
||||
<sub-class-of type="application/x-cd-image"/>
|
||||
<generic-icon name="application-x-cd-image"/>
|
||||
<glob pattern="*.jso"/>
|
||||
<magic priority="50">
|
||||
<match value="JISO" type="string" offset="0">
|
||||
<!-- TODO: Verify this. -->
|
||||
<match value="0x30" type="little32" offset="32"/>
|
||||
</match>
|
||||
</magic>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-psp-ziso-image">
|
||||
<comment>PlayStation Portable ZISO disc image</comment>
|
||||
<sub-class-of type="application/x-cd-image"/>
|
||||
@ -584,15 +605,6 @@
|
||||
</match>
|
||||
</magic>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-psp-dax-image">
|
||||
<comment>PlayStation Portable DAX disc image</comment>
|
||||
<sub-class-of type="application/x-cd-image"/>
|
||||
<generic-icon name="application-x-cd-image"/>
|
||||
<glob pattern="*.dax"/>
|
||||
<magic priority="50">
|
||||
<match value="0x41445800" type="big32" offset="0"/>
|
||||
</magic>
|
||||
</mime-type>
|
||||
|
||||
<!-- *** Other *** -->
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user