mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-03-01 15:30:31 +01:00
More cleaning up, add preliminary MDEC API
This commit is contained in:
parent
d6b66d4f18
commit
a747dabd76
291
CMakeLists.txt
291
CMakeLists.txt
@ -24,36 +24,12 @@ project(
|
||||
HOMEPAGE_URL "https://github.com/spicyjpeg/573in1"
|
||||
)
|
||||
|
||||
include(cmake/options.cmake)
|
||||
|
||||
## Source files
|
||||
|
||||
set(
|
||||
RELEASE_INFO "${PROJECT_NAME} ${PROJECT_VERSION} - (C) 2022-2024 spicyjpeg"
|
||||
CACHE STRING "Executable description and version string (optional)"
|
||||
)
|
||||
set(
|
||||
RELEASE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}"
|
||||
CACHE STRING "CD-ROM image and release package file name"
|
||||
)
|
||||
|
||||
string(TOUPPER "${RELEASE_NAME}" _cdVolumeName)
|
||||
string(REGEX REPLACE "[^0-9A-Z_]" "_" _cdVolumeName "${_cdVolumeName}")
|
||||
set(
|
||||
CD_VOLUME_NAME "${_cdVolumeName}"
|
||||
CACHE STRING "CD-ROM image volume label"
|
||||
)
|
||||
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
find_program(
|
||||
CHDMAN_PATH chdman
|
||||
PATHS
|
||||
"C:/Program Files/MAME"
|
||||
"C:/Program Files (x86)/MAME"
|
||||
"/opt/mame"
|
||||
DOC "Path to MAME chdman tool (optional)"
|
||||
)
|
||||
|
||||
## Files common to all executables
|
||||
|
||||
add_library(
|
||||
common OBJECT
|
||||
ps1Sources
|
||||
#src/libc/crt0.c
|
||||
src/libc/cxxsupport.cpp
|
||||
src/libc/malloc.c
|
||||
@ -66,75 +42,61 @@ add_library(
|
||||
src/ps1/system.s
|
||||
src/ps1/unhandledexc.c
|
||||
)
|
||||
target_include_directories(
|
||||
common PUBLIC
|
||||
src
|
||||
src/libc
|
||||
set(
|
||||
vendorSources
|
||||
src/vendor/ff.c
|
||||
src/vendor/ffunicode.c
|
||||
src/vendor/miniz.c
|
||||
src/vendor/printf.c
|
||||
src/vendor/qrcodegen.c
|
||||
)
|
||||
target_compile_options(
|
||||
common PUBLIC
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
$<$<COMPILE_LANGUAGE:CXX>:
|
||||
-Wno-pmf-conversions
|
||||
>
|
||||
|
||||
set(
|
||||
commonSources
|
||||
src/common/args.cpp
|
||||
src/common/gpu.cpp
|
||||
src/common/gpufont.cpp
|
||||
src/common/io.cpp
|
||||
src/common/ioboard.cpp
|
||||
src/common/mdec.cpp
|
||||
src/common/mdec.s
|
||||
src/common/pad.cpp
|
||||
src/common/rom.cpp
|
||||
src/common/romdrivers.cpp
|
||||
src/common/spu.cpp
|
||||
)
|
||||
target_compile_definitions(
|
||||
common PUBLIC
|
||||
VERSION="${PROJECT_VERSION}"
|
||||
EXTERNAL_DATA_DIR="hdd:/${PROJECT_NAME}"
|
||||
)
|
||||
link_libraries(common)
|
||||
|
||||
function(addPS1Executable name address stackTop)
|
||||
add_executable(${name} ${ARGN})
|
||||
target_link_options(${name} PRIVATE -Ttext=0x${address})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${name} POST_BUILD
|
||||
BYPRODUCTS "${name}.psexe"
|
||||
COMMAND
|
||||
"${Python3_EXECUTABLE}"
|
||||
"${PROJECT_SOURCE_DIR}/tools/convertExecutable.py"
|
||||
-r "${RELEASE_INFO}"
|
||||
-s 0x${stackTop}
|
||||
"$<TARGET_FILE:${name}>"
|
||||
"${name}.psexe"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
## Main executable
|
||||
|
||||
# IMPORTANT: these addresses assume the boot executable's size (including code,
|
||||
# heap and stack allocations as well as the resource archive) is <576 KB
|
||||
# (0x90000 bytes).
|
||||
addPS1Executable(
|
||||
main 800a0000 801dfff0
|
||||
set(
|
||||
fsSources
|
||||
src/common/fs/fat.cpp
|
||||
src/common/fs/file.cpp
|
||||
src/common/fs/iso9660.cpp
|
||||
src/common/fs/misc.cpp
|
||||
src/common/fs/zip.cpp
|
||||
)
|
||||
set(
|
||||
storageSources
|
||||
src/common/storage/ata.cpp
|
||||
src/common/storage/atapi.cpp
|
||||
src/common/storage/device.cpp
|
||||
)
|
||||
set(
|
||||
utilSources
|
||||
src/common/util/hash.cpp
|
||||
src/common/util/log.cpp
|
||||
src/common/util/misc.cpp
|
||||
src/common/util/string.cpp
|
||||
src/common/util/string.s
|
||||
src/common/util/tween.cpp
|
||||
src/common/args.cpp
|
||||
src/common/gpu.cpp
|
||||
src/common/gpufont.cpp
|
||||
src/common/io.cpp
|
||||
src/common/ioboard.cpp
|
||||
src/common/pad.cpp
|
||||
src/common/rom.cpp
|
||||
src/common/romdrivers.cpp
|
||||
src/common/spu.cpp
|
||||
)
|
||||
|
||||
set(
|
||||
mainSources
|
||||
${ps1Sources}
|
||||
${vendorSources}
|
||||
${commonSources}
|
||||
${fsSources}
|
||||
${storageSources}
|
||||
${utilSources}
|
||||
src/libc/crt0.c
|
||||
src/main/app/app.cpp
|
||||
src/main/app/cartactions.cpp
|
||||
@ -155,82 +117,109 @@ addPS1Executable(
|
||||
src/main/uibase.cpp
|
||||
src/main/uicommon.cpp
|
||||
src/main/uimodals.cpp
|
||||
src/vendor/ff.c
|
||||
src/vendor/ffunicode.c
|
||||
src/vendor/miniz.c
|
||||
src/vendor/printf.c
|
||||
src/vendor/qrcodegen.c
|
||||
)
|
||||
target_compile_definitions(
|
||||
main PRIVATE
|
||||
# Logging options
|
||||
ENABLE_APP_LOGGING=1
|
||||
ENABLE_CART_IO_LOGGING=1
|
||||
ENABLE_CART_DATA_LOGGING=1
|
||||
ENABLE_IO_LOGGING=1
|
||||
ENABLE_ROM_LOGGING=1
|
||||
ENABLE_STORAGE_LOGGING=1
|
||||
ENABLE_FS_LOGGING=1
|
||||
# Security cartridge driver options
|
||||
#ENABLE_DUMMY_CART_DRIVER=1
|
||||
ENABLE_X76F041_CART_DRIVER=1
|
||||
#ENABLE_X76F100_CART_DRIVER=1
|
||||
ENABLE_ZS01_CART_DRIVER=1
|
||||
# Misc. options
|
||||
ENABLE_LOG_BUFFER=1
|
||||
#ENABLE_ARGV=1
|
||||
#ENABLE_PCDRV=1
|
||||
ENABLE_PS1_CONTROLLER=1
|
||||
ENABLE_AUTOBOOT=1
|
||||
)
|
||||
|
||||
## Boot stub and executable launchers
|
||||
|
||||
# NOTE: in order to make sure -Os is passed after -Og or -O3 (see
|
||||
# cmake/setup.cmake) and thus overrides it, it must be added to a separate
|
||||
# target rather than directly to the executables.
|
||||
add_library(bootFlags INTERFACE)
|
||||
target_compile_options(bootFlags INTERFACE -Os)
|
||||
target_compile_definitions(
|
||||
bootFlags INTERFACE
|
||||
$<$<CONFIG:Debug>:
|
||||
#ENABLE_ARGV=1
|
||||
#ENABLE_LOGGING=1
|
||||
NDEBUG=1
|
||||
>
|
||||
)
|
||||
|
||||
function(addLauncher address stackTop)
|
||||
addPS1Executable(
|
||||
launcher${address} ${address} ${stackTop}
|
||||
src/common/storage/ata.cpp
|
||||
src/common/storage/atapi.cpp
|
||||
src/common/storage/device.cpp
|
||||
src/common/util/hash.cpp
|
||||
src/common/util/misc.cpp
|
||||
set(
|
||||
launcherSources
|
||||
${ps1Sources}
|
||||
${storageSources}
|
||||
${utilSources}
|
||||
src/common/args.cpp
|
||||
src/common/io.cpp
|
||||
src/launcher/exchandler.s
|
||||
src/launcher/main.cpp
|
||||
src/libc/crt0.c
|
||||
src/vendor/printf.c
|
||||
)
|
||||
target_link_libraries(launcher${address} PRIVATE bootFlags)
|
||||
set(
|
||||
bootStubSources
|
||||
${ps1Sources}
|
||||
${utilSources}
|
||||
src/boot/crt0.s
|
||||
src/boot/main.cpp
|
||||
src/common/io.cpp
|
||||
)
|
||||
|
||||
## Flags and compile-time options
|
||||
|
||||
set(
|
||||
commonOptions
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
$<$<COMPILE_LANGUAGE:CXX>:
|
||||
-Wno-pmf-conversions
|
||||
-Wno-delete-non-virtual-dtor
|
||||
>
|
||||
)
|
||||
|
||||
add_library(mainFlags INTERFACE)
|
||||
add_library(subExecutableFlags INTERFACE)
|
||||
|
||||
target_include_directories(mainFlags INTERFACE src src/libc)
|
||||
target_include_directories(subExecutableFlags INTERFACE src src/libc)
|
||||
|
||||
# NOTE: in order to make sure -Os is passed after -Og or -O3 (see
|
||||
# cmake/setup.cmake) and thus overrides it, it must be added to a separate
|
||||
# target rather than directly to the executables.
|
||||
target_compile_options(mainFlags INTERFACE ${commonOptions})
|
||||
target_compile_options(subExecutableFlags INTERFACE ${commonOptions} -Os)
|
||||
|
||||
target_compile_definitions(
|
||||
mainFlags INTERFACE
|
||||
${mainOptions}
|
||||
VERSION="${PROJECT_VERSION}"
|
||||
EXTERNAL_DATA_DIR="hdd:/${PROJECT_NAME}"
|
||||
)
|
||||
target_compile_definitions(
|
||||
subExecutableFlags INTERFACE
|
||||
${subExecutableOptions}
|
||||
NDEBUG=1
|
||||
)
|
||||
|
||||
## Main executables
|
||||
|
||||
function(addPS1Executable name address stackTop)
|
||||
add_executable (${name} ${ARGN})
|
||||
target_link_options(${name} PRIVATE -Ttext=0x${address})
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${name} POST_BUILD
|
||||
BYPRODUCTS "${name}.psexe"
|
||||
COMMAND
|
||||
"${Python3_EXECUTABLE}"
|
||||
"${PROJECT_SOURCE_DIR}/tools/convertExecutable.py"
|
||||
-r "${RELEASE_INFO}"
|
||||
-s 0x${stackTop}
|
||||
"$<TARGET_FILE:${name}>"
|
||||
"${name}.psexe"
|
||||
VERBATIM
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# IMPORTANT: these addresses assume the launcher's total size (including code,
|
||||
# heap and stack allocations, but excluding the executable header) is <12 KB
|
||||
# (0x3000 bytes).
|
||||
addLauncher(801fd000 801ffff0)
|
||||
addLauncher(803fd000 803ffff0)
|
||||
# IMPORTANT: these addresses assume the boot executable's size (including code,
|
||||
# heap and stack allocations as well as the resource archive, but excluding the
|
||||
# executable header) is 576 KB (0x90000 bytes) or less, and that each launcher's
|
||||
# size is 12 KB (0x3000 bytes) or less.
|
||||
addPS1Executable(main 800a0000 801dfff0 ${mainSources})
|
||||
addPS1Executable(launcher801fd000 801fd000 801ffff0)
|
||||
addPS1Executable(launcher803fd000 803fd000 803ffff0)
|
||||
|
||||
## Boot stub and resource archive
|
||||
add_library(launcher OBJECT ${launcherSources})
|
||||
add_library(bootStub OBJECT ${bootStubSources})
|
||||
|
||||
target_link_libraries(main PUBLIC mainFlags)
|
||||
target_link_libraries(launcher PUBLIC subExecutableFlags)
|
||||
target_link_libraries(bootStub PUBLIC subExecutableFlags)
|
||||
|
||||
target_link_libraries(launcher801fd000 PUBLIC launcher)
|
||||
target_link_libraries(launcher803fd000 PUBLIC launcher)
|
||||
|
||||
## Boot stubs and resource archives
|
||||
|
||||
file(GLOB_RECURSE assetList RELATIVE "${PROJECT_SOURCE_DIR}" assets/*)
|
||||
|
||||
configure_file(assets/about.txt about.txt NEWLINE_STYLE LF)
|
||||
|
||||
function(addBootStub name resourceName)
|
||||
function(addBuildVariant name resourceName)
|
||||
configure_file(${resourceName}.json ${resourceName}.json ESCAPE_QUOTES)
|
||||
|
||||
add_custom_command(
|
||||
@ -250,23 +239,17 @@ function(addBootStub name resourceName)
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
addPS1Executable(
|
||||
${name} 80010000 0
|
||||
src/boot/crt0.s
|
||||
src/boot/main.cpp
|
||||
src/common/util/misc.cpp
|
||||
src/common/util/string.cpp
|
||||
src/common/io.cpp
|
||||
)
|
||||
addPS1Executable(${name} 80010000 0)
|
||||
addBinaryFile(
|
||||
${name} _resourceArchive _resourceArchiveLength
|
||||
"${PROJECT_BINARY_DIR}/${resourceName}.zip"
|
||||
)
|
||||
target_link_libraries(${name} PRIVATE bootFlags)
|
||||
|
||||
target_link_libraries(${name} PUBLIC bootStub)
|
||||
endfunction()
|
||||
|
||||
addBootStub("${RELEASE_NAME}" resources)
|
||||
addBootStub("${RELEASE_NAME}-tiny" resourcestiny)
|
||||
addBuildVariant("${RELEASE_NAME}" resources)
|
||||
addBuildVariant("${RELEASE_NAME}-tiny" resourcestiny)
|
||||
|
||||
## CD-ROM image
|
||||
|
||||
|
145
cmake/options.cmake
Normal file
145
cmake/options.cmake
Normal file
@ -0,0 +1,145 @@
|
||||
# 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
#
|
||||
# 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
## External tools
|
||||
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
||||
|
||||
find_program(
|
||||
CHDMAN_PATH chdman
|
||||
PATHS
|
||||
"C:/Program Files/MAME"
|
||||
"C:/Program Files (x86)/MAME"
|
||||
"/opt/mame"
|
||||
DOC "Path to MAME chdman tool for generating CHD image (optional)"
|
||||
)
|
||||
|
||||
## Release information
|
||||
|
||||
set(
|
||||
RELEASE_INFO "${PROJECT_NAME} ${PROJECT_VERSION} - (C) 2022-2024 spicyjpeg"
|
||||
CACHE STRING "Executable description and version string, placed in the \
|
||||
executable header (optional)"
|
||||
)
|
||||
set(
|
||||
RELEASE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}"
|
||||
CACHE STRING "CD-ROM image and release package file name"
|
||||
)
|
||||
|
||||
string(TOUPPER "${RELEASE_NAME}" _cdVolumeName)
|
||||
string(REGEX REPLACE "[^0-9A-Z_]" "_" _cdVolumeName "${_cdVolumeName}")
|
||||
|
||||
set(
|
||||
CD_VOLUME_NAME "${_cdVolumeName}"
|
||||
CACHE STRING "CD-ROM image volume label"
|
||||
)
|
||||
|
||||
## Compile-time options
|
||||
|
||||
set(
|
||||
ENABLE_LOG_BUFFER ON
|
||||
CACHE BOOL "Buffer the last few log messages in memory and enable the \
|
||||
on-screen log window"
|
||||
)
|
||||
set(
|
||||
ENABLE_APP_LOGGING ON
|
||||
CACHE BOOL "Log messages from application code"
|
||||
)
|
||||
set(
|
||||
ENABLE_CART_IO_LOGGING ON
|
||||
CACHE BOOL "Log messages from security cartridge drivers"
|
||||
)
|
||||
set(
|
||||
ENABLE_CART_DATA_LOGGING ON
|
||||
CACHE BOOL "Log messages from security cartridge data parsers and builders"
|
||||
)
|
||||
set(
|
||||
ENABLE_IO_LOGGING ON
|
||||
CACHE BOOL "Log messages from System 573 and I/O board drivers"
|
||||
)
|
||||
set(
|
||||
ENABLE_ROM_LOGGING ON
|
||||
CACHE BOOL "Log messages from ROM/flash drivers"
|
||||
)
|
||||
set(
|
||||
ENABLE_STORAGE_LOGGING ON
|
||||
CACHE BOOL "Log messages from IDE/ATAPI and other block device drivers"
|
||||
)
|
||||
set(
|
||||
ENABLE_FS_LOGGING ON
|
||||
CACHE BOOL "Log messages from filesystem drivers"
|
||||
)
|
||||
|
||||
set(
|
||||
ENABLE_DUMMY_CART_DRIVER OFF
|
||||
CACHE BOOL "Enable support for simulating a dummy security cartridge (if \
|
||||
data/test.573 is present in the resource archive)"
|
||||
)
|
||||
set(
|
||||
ENABLE_X76F041_CART_DRIVER ON
|
||||
CACHE BOOL "Enable support for X76F041 security cartridges"
|
||||
)
|
||||
set(
|
||||
ENABLE_X76F100_CART_DRIVER OFF
|
||||
CACHE BOOL "Enable support for X76F100 security cartridges"
|
||||
)
|
||||
set(
|
||||
ENABLE_ZS01_CART_DRIVER ON
|
||||
CACHE BOOL "Enable support for ZS01 security cartridges"
|
||||
)
|
||||
|
||||
set(
|
||||
ENABLE_ARGV_PARSER OFF
|
||||
CACHE BOOL "Pass any command-line arguments given to the boot stub (using \
|
||||
\$a0 and \$a1 as argc/argv) to the main executable"
|
||||
)
|
||||
set(
|
||||
ENABLE_AUTOBOOT ON
|
||||
CACHE BOOL "Enable support for automatic launching of executables on IDE \
|
||||
and flash devices (if enabled via DIP switches)"
|
||||
)
|
||||
set(
|
||||
ENABLE_PCDRV OFF
|
||||
CACHE BOOL "Enable support for browsing the debugger or emulator's host \
|
||||
filesystem through the PCDRV API"
|
||||
)
|
||||
set(
|
||||
ENABLE_PS1_CONTROLLER ON
|
||||
CACHE BOOL "Enable support for PS1/PS2 controllers wired to the SIO0 header"
|
||||
)
|
||||
|
||||
set(
|
||||
mainOptions
|
||||
$<$<BOOL:${ENABLE_LOG_BUFFER} >:ENABLE_LOG_BUFFER=1>
|
||||
$<$<BOOL:${ENABLE_APP_LOGGING} >:ENABLE_APP_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_CART_IO_LOGGING} >:ENABLE_CART_IO_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_CART_DATA_LOGGING} >:ENABLE_CART_DATA_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_IO_LOGGING} >:ENABLE_IO_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_ROM_LOGGING} >:ENABLE_ROM_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_STORAGE_LOGGING} >:ENABLE_STORAGE_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_FS_LOGGING} >:ENABLE_FS_LOGGING=1>
|
||||
$<$<BOOL:${ENABLE_DUMMY_CART_DRIVER} >:ENABLE_DUMMY_CART_DRIVER=1>
|
||||
$<$<BOOL:${ENABLE_X76F041_CART_DRIVER}>:ENABLE_X76F041_CART_DRIVER=1>
|
||||
$<$<BOOL:${ENABLE_X76F100_CART_DRIVER}>:ENABLE_X76F100_CART_DRIVER=1>
|
||||
$<$<BOOL:${ENABLE_ZS01_CART_DRIVER} >:ENABLE_ZS01_CART_DRIVER=1>
|
||||
$<$<BOOL:${ENABLE_AUTOBOOT} >:ENABLE_AUTOBOOT=1>
|
||||
$<$<BOOL:${ENABLE_PCDRV} >:ENABLE_PCDRV=1>
|
||||
$<$<BOOL:${ENABLE_PS1_CONTROLLER} >:ENABLE_PS1_CONTROLLER=1>
|
||||
)
|
||||
set(
|
||||
subExecutableOptions
|
||||
$<$<BOOL:${ENABLE_ARGV_PARSER}>:ENABLE_ARGV_PARSER=1>
|
||||
)
|
@ -90,7 +90,7 @@ int main(int argc, const char **argv) {
|
||||
util::hexValueToString(&_lengthArg[16], _resourceArchiveLength, 8);
|
||||
loader.addArgument(_lengthArg);
|
||||
|
||||
#ifdef ENABLE_ARGV
|
||||
#ifdef ENABLE_ARGV_PARSER
|
||||
for (; argc > 0; argc--) {
|
||||
if (!loader.copyArgument(*(argv++)))
|
||||
break;
|
||||
|
@ -246,17 +246,17 @@ bool FATProvider::createDirectory(const char *path) {
|
||||
}
|
||||
|
||||
File *FATProvider::openFile(const char *path, uint32_t flags) {
|
||||
auto _file = new FATFile();
|
||||
auto error = f_open(&_fs, &(_file->_fd), path, uint8_t(flags));
|
||||
auto file = new FATFile();
|
||||
auto error = f_open(&_fs, &(file->_fd), path, uint8_t(flags));
|
||||
|
||||
if (error) {
|
||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path);
|
||||
delete _file;
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_file->size = f_size(&(_file->_fd));
|
||||
return _file;
|
||||
file->size = f_size(&(file->_fd));
|
||||
return file;
|
||||
}
|
||||
|
||||
/* FatFs library API glue */
|
||||
|
@ -52,56 +52,54 @@ Directory::~Directory(void) {
|
||||
|
||||
/* Base file and asset provider classes */
|
||||
|
||||
uint32_t currentSPUOffset = spu::DUMMY_BLOCK_END;
|
||||
|
||||
Provider::~Provider(void) {
|
||||
close();
|
||||
}
|
||||
|
||||
size_t Provider::loadData(util::Data &output, const char *path) {
|
||||
auto _file = openFile(path, READ);
|
||||
auto file = openFile(path, READ);
|
||||
|
||||
if (!_file)
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
assert(_file->size <= SIZE_MAX);
|
||||
if (!output.allocate(size_t(_file->size))) {
|
||||
_file->close();
|
||||
delete _file;
|
||||
assert(file->size <= SIZE_MAX);
|
||||
if (!output.allocate(size_t(file->size))) {
|
||||
file->close();
|
||||
delete file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t actualLength = _file->read(output.ptr, output.length);
|
||||
size_t actualLength = file->read(output.ptr, output.length);
|
||||
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
return actualLength;
|
||||
}
|
||||
|
||||
size_t Provider::loadData(void *output, size_t length, const char *path) {
|
||||
auto _file = openFile(path, READ);
|
||||
auto file = openFile(path, READ);
|
||||
|
||||
if (!_file)
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
assert(_file->size >= length);
|
||||
size_t actualLength = _file->read(output, length);
|
||||
assert(file->size >= length);
|
||||
size_t actualLength = file->read(output, length);
|
||||
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
return actualLength;
|
||||
}
|
||||
|
||||
size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
||||
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
||||
auto file = openFile(path, WRITE | ALLOW_CREATE);
|
||||
|
||||
if (!_file)
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
size_t actualLength = _file->write(input, length);
|
||||
size_t actualLength = file->write(input, length);
|
||||
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
return actualLength;
|
||||
}
|
||||
|
||||
@ -118,22 +116,26 @@ size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
||||
data.destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t uploadLength = 0;
|
||||
|
||||
if (header->flags & (1 << 3)) {
|
||||
auto clut = reinterpret_cast<const gpu::TIMSectionHeader *>(section);
|
||||
uploadLength += gpu::upload(clut->vram, &clut[1], true);
|
||||
|
||||
gpu::upload(clut->vram, &clut[1], true);
|
||||
section += clut->length;
|
||||
}
|
||||
|
||||
auto image = reinterpret_cast<const gpu::TIMSectionHeader *>(section);
|
||||
|
||||
gpu::upload(image->vram, &image[1], true);
|
||||
uploadLength += gpu::upload(image->vram, &image[1], true);
|
||||
|
||||
data.destroy();
|
||||
return data.length;
|
||||
return uploadLength;
|
||||
}
|
||||
|
||||
size_t Provider::loadVAG(spu::Sound &output, const char *path) {
|
||||
size_t Provider::loadVAG(
|
||||
spu::Sound &output, uint32_t offset, const char *path
|
||||
) {
|
||||
// Sounds should be decompressed and uploaded to the SPU one chunk at a
|
||||
// time, but whatever.
|
||||
util::Data data;
|
||||
@ -144,17 +146,16 @@ size_t Provider::loadVAG(spu::Sound &output, const char *path) {
|
||||
auto header = data.as<const spu::VAGHeader>();
|
||||
auto body = reinterpret_cast<const uint32_t *>(&header[1]);
|
||||
|
||||
if (!output.initFromVAGHeader(header, currentSPUOffset)) {
|
||||
if (!output.initFromVAGHeader(header, offset)) {
|
||||
data.destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
currentSPUOffset += spu::upload(
|
||||
currentSPUOffset, body, data.length - sizeof(spu::VAGHeader), true
|
||||
);
|
||||
auto uploadLength =
|
||||
spu::upload(offset, body, data.length - sizeof(spu::VAGHeader), true);
|
||||
|
||||
data.destroy();
|
||||
return data.length;
|
||||
return uploadLength;
|
||||
}
|
||||
|
||||
struct [[gnu::packed]] BMPHeader {
|
||||
@ -186,16 +187,16 @@ public:
|
||||
};
|
||||
|
||||
size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
||||
auto _file = openFile(path, WRITE | ALLOW_CREATE);
|
||||
auto file = openFile(path, WRITE | ALLOW_CREATE);
|
||||
|
||||
if (!_file)
|
||||
if (!file)
|
||||
return 0;
|
||||
|
||||
BMPHeader header;
|
||||
|
||||
header.init(rect.w, rect.h, 16);
|
||||
|
||||
size_t length = _file->write(&header, sizeof(header));
|
||||
size_t length = file->write(&header, sizeof(header));
|
||||
util::Data buffer;
|
||||
|
||||
if (buffer.allocate<uint16_t>(rect.w + 32)) {
|
||||
@ -224,14 +225,14 @@ size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
||||
*(ptr++) = newValue;
|
||||
}
|
||||
|
||||
length += _file->write(buffer.ptr, lineLength);
|
||||
length += file->write(buffer.ptr, lineLength);
|
||||
}
|
||||
|
||||
buffer.destroy();
|
||||
}
|
||||
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
@ -113,8 +113,6 @@ public:
|
||||
|
||||
/* Base file and asset provider classes */
|
||||
|
||||
extern uint32_t currentSPUOffset;
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
FileSystemType type;
|
||||
@ -153,7 +151,7 @@ public:
|
||||
virtual size_t saveData(const void *input, size_t length, const char *path);
|
||||
|
||||
size_t loadTIM(gpu::Image &output, const char *path);
|
||||
size_t loadVAG(spu::Sound &output, const char *path);
|
||||
size_t loadVAG(spu::Sound &output, uint32_t offset, const char *path);
|
||||
size_t saveVRAMBMP(gpu::RectWH &rect, const char *path);
|
||||
};
|
||||
|
||||
|
@ -124,12 +124,12 @@ bool ZIPProvider::init(File *file) {
|
||||
_zip.m_pRead = [](
|
||||
void *opaque, uint64_t offset, void *output, size_t length
|
||||
) -> size_t {
|
||||
auto _file = reinterpret_cast<File *>(opaque);
|
||||
auto file = reinterpret_cast<File *>(opaque);
|
||||
|
||||
if (_file->seek(offset) != offset)
|
||||
if (file->seek(offset) != offset)
|
||||
return 0;
|
||||
|
||||
return _file->read(output, length);
|
||||
return file->read(output, length);
|
||||
};
|
||||
|
||||
if (!mz_zip_reader_init(&_zip, file->size, _ZIP_FLAGS)) {
|
||||
|
244
src/common/mdec.cpp
Normal file
244
src/common/mdec.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "common/util/templates.hpp"
|
||||
#include "common/mdec.hpp"
|
||||
#include "ps1/registers.h"
|
||||
#include "ps1/system.h"
|
||||
|
||||
namespace mdec {
|
||||
|
||||
/* IDCT matrix and quantization table */
|
||||
|
||||
#define SF0 0x5a82 // cos(0/16 * pi) * sqrt(2)
|
||||
#define SF1 0x7d8a // cos(1/16 * pi) * 2
|
||||
#define SF2 0x7641 // cos(2/16 * pi) * 2
|
||||
#define SF3 0x6a6d // cos(3/16 * pi) * 2
|
||||
#define SF4 0x5a82 // cos(4/16 * pi) * 2
|
||||
#define SF5 0x471c // cos(5/16 * pi) * 2
|
||||
#define SF6 0x30fb // cos(6/16 * pi) * 2
|
||||
#define SF7 0x18f8 // cos(7/16 * pi) * 2
|
||||
|
||||
static const int16_t _IDCT_TABLE[]{
|
||||
SF0, SF0, SF0, SF0, SF0, SF0, SF0, SF0,
|
||||
SF1, SF3, SF5, SF7, -SF7, -SF5, -SF3, -SF1,
|
||||
SF2, SF6, -SF6, -SF2, -SF2, -SF6, SF6, SF2,
|
||||
SF3, -SF7, -SF1, -SF5, SF5, SF1, SF7, -SF3,
|
||||
SF4, -SF4, -SF4, SF4, SF4, -SF4, -SF4, SF4,
|
||||
SF5, -SF1, SF7, SF3, -SF3, -SF7, SF1, -SF5,
|
||||
SF6, -SF2, SF2, -SF6, -SF6, SF2, -SF2, SF6,
|
||||
SF7, -SF5, SF3, -SF1, SF1, -SF3, SF5, -SF7
|
||||
};
|
||||
|
||||
// The BS v2/v3 quantization table is based on the MPEG-1 table, with the only
|
||||
// difference being the DC coefficient (2 instead of 8). Quantization tables are
|
||||
// stored in zigzag order, rather than row- or column-major.
|
||||
static const uint8_t _BS_QUANT_TABLE[]{
|
||||
2, 16, 16, 19, 16, 19, 22, 22,
|
||||
22, 22, 22, 22, 26, 24, 26, 27,
|
||||
27, 27, 26, 26, 26, 26, 27, 27,
|
||||
27, 29, 29, 29, 34, 34, 34, 29,
|
||||
29, 29, 27, 27, 29, 29, 32, 32,
|
||||
34, 34, 37, 38, 37, 35, 35, 34,
|
||||
35, 38, 38, 40, 40, 40, 48, 48,
|
||||
46, 46, 56, 56, 58, 69, 69, 83
|
||||
};
|
||||
|
||||
/* Basic API */
|
||||
|
||||
static constexpr int _DMA_CHUNK_SIZE = 32;
|
||||
static constexpr int _DMA_TIMEOUT = 100000;
|
||||
|
||||
void init(void) {
|
||||
MDEC1 = MDEC_CTRL_RESET;
|
||||
MDEC1 = MDEC_CTRL_DMA_OUT | MDEC_CTRL_DMA_IN;
|
||||
|
||||
MDEC0 = MDEC_CMD_SET_IDCT_TABLE;
|
||||
feed(_IDCT_TABLE, sizeof(_IDCT_TABLE), true);
|
||||
|
||||
MDEC0 = MDEC_CMD_SET_QUANT_TABLE | MDEC_CMD_FLAG_USE_CHROMA;
|
||||
feed(_BS_QUANT_TABLE, sizeof(_BS_QUANT_TABLE), true);
|
||||
feed(_BS_QUANT_TABLE, sizeof(_BS_QUANT_TABLE), true);
|
||||
}
|
||||
|
||||
size_t feed(const void *data, size_t length, bool wait) {
|
||||
length /= 4;
|
||||
|
||||
util::assertAligned<uint32_t>(data);
|
||||
#if 0
|
||||
assert(!(length % _DMA_CHUNK_SIZE));
|
||||
#else
|
||||
length = (length + _DMA_CHUNK_SIZE - 1) / _DMA_CHUNK_SIZE;
|
||||
#endif
|
||||
|
||||
if (!waitForDMATransfer(DMA_MDEC_IN, _DMA_TIMEOUT))
|
||||
return 0;
|
||||
|
||||
DMA_MADR(DMA_MDEC_IN) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_MDEC_IN) = util::concat4(_DMA_CHUNK_SIZE, length);
|
||||
DMA_CHCR(DMA_MDEC_IN) = 0
|
||||
| DMA_CHCR_WRITE
|
||||
| DMA_CHCR_MODE_SLICE
|
||||
| DMA_CHCR_ENABLE;
|
||||
|
||||
if (wait)
|
||||
waitForDMATransfer(DMA_MDEC_IN, _DMA_TIMEOUT);
|
||||
|
||||
return length * _DMA_CHUNK_SIZE * 4;
|
||||
}
|
||||
|
||||
/* MDEC bitstream decompressor */
|
||||
|
||||
static constexpr inline uint8_t dc(int luma, int chroma) {
|
||||
return (chroma & 15) | (luma << 4);
|
||||
}
|
||||
static constexpr inline uint16_t ac(int rl, int coeff) {
|
||||
return (coeff & 0x3ff) | (rl << 10);
|
||||
}
|
||||
static constexpr inline uint32_t acl(int rl, int coeff, int length) {
|
||||
return (coeff & 0x3ff) | (rl << 10) | (length << 16);
|
||||
}
|
||||
|
||||
#define DC2(luma, chroma) dc (luma, chroma), dc (luma, chroma)
|
||||
#define DC4(luma, chroma) DC2 (luma, chroma), DC2 (luma, chroma)
|
||||
#define DC8(luma, chroma) DC4 (luma, chroma), DC4 (luma, chroma)
|
||||
#define DC16(luma, chroma) DC8 (luma, chroma), DC8 (luma, chroma)
|
||||
#define DC32(luma, chroma) DC16(luma, chroma), DC16(luma, chroma)
|
||||
|
||||
#define AC2(rl, coeff) ac (rl, coeff), ac (rl, coeff)
|
||||
#define AC4(rl, coeff) AC2(rl, coeff), AC2(rl, coeff)
|
||||
#define AC8(rl, coeff) AC4(rl, coeff), AC4(rl, coeff)
|
||||
#define PAIR(rl, coeff) ac (rl, coeff), ac (rl, -(coeff))
|
||||
#define PAIR2(rl, coeff) AC2(rl, coeff), AC2(rl, -(coeff))
|
||||
#define PAIR4(rl, coeff) AC4(rl, coeff), AC4(rl, -(coeff))
|
||||
#define PAIR8(rl, coeff) AC8(rl, coeff), AC8(rl, -(coeff))
|
||||
|
||||
#define ACL2(rl, coeff, length) acl (rl, coeff, length), acl (rl, coeff, length)
|
||||
#define ACL4(rl, coeff, length) ACL2(rl, coeff, length), ACL2(rl, coeff, length)
|
||||
#define ACL8(rl, coeff, length) ACL4(rl, coeff, length), ACL4(rl, coeff, length)
|
||||
#define PAIRL(rl, coeff, length) acl (rl, coeff, length), acl (rl, -(coeff), length)
|
||||
#define PAIRL2(rl, coeff, length) ACL2(rl, coeff, length), ACL2(rl, -(coeff), length)
|
||||
#define PAIRL4(rl, coeff, length) ACL4(rl, coeff, length), ACL4(rl, -(coeff), length)
|
||||
#define PAIRL8(rl, coeff, length) ACL8(rl, coeff, length), ACL8(rl, -(coeff), length)
|
||||
|
||||
struct BSHuffmanTable {
|
||||
public:
|
||||
uint16_t ac0[2];
|
||||
uint32_t ac2[8], ac3 [64];
|
||||
uint16_t ac4[8], ac5 [8], ac7 [16], ac8 [32];
|
||||
uint16_t ac9[32], ac10[32], ac11[32], ac12[32];
|
||||
|
||||
uint8_t dcValues[128], dcLengths[9];
|
||||
};
|
||||
|
||||
static const BSHuffmanTable _HUFFMAN_TABLE{
|
||||
.ac0 = {
|
||||
PAIR( 0, 1) // 11x
|
||||
},
|
||||
.ac2 = {
|
||||
PAIRL ( 0, 2, 5), PAIRL ( 2, 1, 5), // 010xx
|
||||
PAIRL2( 1, 1, 4) // 011x-
|
||||
},
|
||||
.ac3 = {
|
||||
// 00100xxxx
|
||||
PAIRL (13, 1, 9), PAIRL ( 0, 6, 9), PAIRL (12, 1, 9), PAIRL (11, 1, 9),
|
||||
PAIRL ( 3, 2, 9), PAIRL ( 1, 3, 9), PAIRL ( 0, 5, 9), PAIRL (10, 1, 9),
|
||||
// 001xxx---
|
||||
PAIRL8( 0, 3, 6), PAIRL8( 4, 1, 6), PAIRL8( 3, 1, 6)
|
||||
},
|
||||
.ac4 = {
|
||||
// 0001xxx
|
||||
PAIR( 7, 1), PAIR( 6, 1), PAIR( 1, 2), PAIR( 5, 1)
|
||||
},
|
||||
.ac5 = {
|
||||
// 00001xxx
|
||||
PAIR( 2, 2), PAIR( 9, 1), PAIR( 0, 4), PAIR( 8, 1)
|
||||
},
|
||||
.ac7 = {
|
||||
// 0000001xxxx
|
||||
PAIR(16, 1), PAIR( 5, 2), PAIR( 0, 7), PAIR( 2, 3),
|
||||
PAIR( 1, 4), PAIR(15, 1), PAIR(14, 1), PAIR( 4, 2)
|
||||
},
|
||||
.ac8 = {
|
||||
// 00000001xxxxx
|
||||
PAIR( 0, 11), PAIR( 8, 2), PAIR( 4, 3), PAIR( 0, 10),
|
||||
PAIR( 2, 4), PAIR( 7, 2), PAIR(21, 1), PAIR(20, 1),
|
||||
PAIR( 0, 9), PAIR(19, 1), PAIR(18, 1), PAIR( 1, 5),
|
||||
PAIR( 3, 3), PAIR( 0, 8), PAIR( 6, 2), PAIR(17, 1)
|
||||
},
|
||||
.ac9 = {
|
||||
// 000000001xxxxx
|
||||
PAIR(10, 2), PAIR( 9, 2), PAIR( 5, 3), PAIR( 3, 4),
|
||||
PAIR( 2, 5), PAIR( 1, 7), PAIR( 1, 6), PAIR( 0, 15),
|
||||
PAIR( 0, 14), PAIR( 0, 13), PAIR( 0, 12), PAIR(26, 1),
|
||||
PAIR(25, 1), PAIR(24, 1), PAIR(23, 1), PAIR(22, 1)
|
||||
},
|
||||
.ac10 = {
|
||||
// 0000000001xxxxx
|
||||
PAIR( 0, 31), PAIR( 0, 30), PAIR( 0, 29), PAIR( 0, 28),
|
||||
PAIR( 0, 27), PAIR( 0, 26), PAIR( 0, 25), PAIR( 0, 24),
|
||||
PAIR( 0, 23), PAIR( 0, 22), PAIR( 0, 21), PAIR( 0, 20),
|
||||
PAIR( 0, 19), PAIR( 0, 18), PAIR( 0, 17), PAIR( 0, 16)
|
||||
},
|
||||
.ac11 = {
|
||||
// 00000000001xxxxx
|
||||
PAIR( 0, 40), PAIR( 0, 39), PAIR( 0, 38), PAIR( 0, 37),
|
||||
PAIR( 0, 36), PAIR( 0, 35), PAIR( 0, 34), PAIR( 0, 33),
|
||||
PAIR( 0, 32), PAIR( 1, 14), PAIR( 1, 13), PAIR( 1, 12),
|
||||
PAIR( 1, 11), PAIR( 1, 10), PAIR( 1, 9), PAIR( 1, 8)
|
||||
},
|
||||
.ac12 = {
|
||||
// 000000000001xxxxx
|
||||
PAIR( 1, 18), PAIR( 1, 17), PAIR( 1, 16), PAIR( 1, 15),
|
||||
PAIR( 6, 3), PAIR(16, 2), PAIR(15, 2), PAIR(14, 2),
|
||||
PAIR(13, 2), PAIR(12, 2), PAIR(11, 2), PAIR(31, 1),
|
||||
PAIR(30, 1), PAIR(29, 1), PAIR(28, 1), PAIR(27, 1)
|
||||
},
|
||||
.dcValues = {
|
||||
DC32(1, 0), // 00-----
|
||||
DC32(2, 1), // 01-----
|
||||
DC16(0, 2), // 100----
|
||||
DC16(3, 2), // 101----
|
||||
DC16(4, 3), // 110----
|
||||
DC8 (5, 4), // 1110---
|
||||
DC4 (6, 5), // 11110--
|
||||
DC2 (7, 6), // 111110-
|
||||
dc (8, 7), // 1111110
|
||||
dc (0, 8) // 1111111(0)
|
||||
},
|
||||
.dcLengths = {
|
||||
dc(3, 2),
|
||||
dc(2, 2),
|
||||
dc(2, 2),
|
||||
dc(3, 3),
|
||||
dc(3, 4),
|
||||
dc(4, 5),
|
||||
dc(5, 6),
|
||||
dc(6, 7),
|
||||
dc(7, 8)
|
||||
}
|
||||
};
|
||||
|
||||
void initBSHuffmanTable(void) {
|
||||
auto table = reinterpret_cast<BSHuffmanTable *>(CACHE_BASE);
|
||||
|
||||
__builtin_memcpy(table, &_HUFFMAN_TABLE, sizeof(BSHuffmanTable));
|
||||
}
|
||||
|
||||
}
|
90
src/common/mdec.hpp
Normal file
90
src/common/mdec.hpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
*
|
||||
* 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "ps1/registers.h"
|
||||
|
||||
namespace mdec {
|
||||
|
||||
/* Basic API */
|
||||
|
||||
void init(void);
|
||||
size_t feed(const void *data, size_t length, bool wait);
|
||||
|
||||
static inline bool isIdle(void) {
|
||||
return !(MDEC1 & MDEC_STAT_BUSY);
|
||||
}
|
||||
|
||||
static inline size_t feedBS(const uint32_t *data, uint32_t flags, bool wait) {
|
||||
size_t length = data[0] & MDEC_CMD_FLAG_LENGTH_BITMASK;
|
||||
|
||||
MDEC0 = MDEC_CMD_DECODE | length | flags;
|
||||
return feed(&data[1], length, wait);
|
||||
}
|
||||
|
||||
/* MDEC bitstream decompressor */
|
||||
|
||||
struct BSHeader {
|
||||
public:
|
||||
uint16_t outputLength;
|
||||
uint16_t mdecCommand;
|
||||
uint16_t quantScale;
|
||||
uint16_t version;
|
||||
};
|
||||
|
||||
enum BSDecompressorError {
|
||||
NO_ERROR = 0,
|
||||
PARTIAL_DATA = 1,
|
||||
DECODE_ERROR = 2
|
||||
};
|
||||
|
||||
extern "C" BSDecompressorError _bsDecompressorStart(
|
||||
void *_this, uint32_t *output, size_t outputLength, const uint32_t *input
|
||||
);
|
||||
extern "C" BSDecompressorError _bsDecompressorResume(
|
||||
void *_this, uint32_t *output, size_t outputLength
|
||||
);
|
||||
|
||||
class BSDecompressor {
|
||||
protected:
|
||||
const uint32_t *_input;
|
||||
|
||||
uint32_t _bits, _nextBits;
|
||||
size_t _remaining;
|
||||
|
||||
uint8_t _isV3;
|
||||
int8_t _bitOffset, _blockIndex, _coeffIndex;
|
||||
|
||||
uint16_t _quantScale;
|
||||
int16_t _lastY, _lastCr, _lastCb;
|
||||
|
||||
public:
|
||||
inline BSDecompressorError decompress(
|
||||
uint32_t *output, const uint32_t *input, size_t outputLength
|
||||
) {
|
||||
return _bsDecompressorStart(this, output, outputLength, input);
|
||||
}
|
||||
inline BSDecompressorError resume(uint32_t *output, size_t outputLength) {
|
||||
return _bsDecompressorResume(this, output, outputLength);
|
||||
}
|
||||
};
|
||||
|
||||
void initBSHuffmanTable(void);
|
||||
|
||||
}
|
573
src/common/mdec.s
Normal file
573
src/common/mdec.s
Normal file
@ -0,0 +1,573 @@
|
||||
# 573in1 - Copyright (C) 2022-2024 spicyjpeg
|
||||
#
|
||||
# 573in1 is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# 573in1 is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# 573in1. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
.set noreorder
|
||||
.set noat
|
||||
|
||||
.set GTE_LZCS, $30 # Leading zero count input
|
||||
.set GTE_LZCR, $31 # Leading zero count output
|
||||
|
||||
.set CACHE_BASE, 0x9f800000
|
||||
|
||||
## Structure definitions
|
||||
|
||||
.set BSHeader_outputLength, 0
|
||||
.set BSHeader_mdecCommand, BSHeader_outputLength + 2
|
||||
.set BSHeader_quantScale, BSHeader_mdecCommand + 2
|
||||
.set BSHeader_version, BSHeader_quantScale + 2
|
||||
.set BSHeader_data0, BSHeader_version + 2
|
||||
.set BSHeader_data1, BSHeader_data0 + 4
|
||||
|
||||
.set BSDecompressor_input, 0
|
||||
.set BSDecompressor_bits, BSDecompressor_input + 4
|
||||
.set BSDecompressor_nextBits, BSDecompressor_bits + 4
|
||||
.set BSDecompressor_remaining, BSDecompressor_nextBits + 4
|
||||
.set BSDecompressor_isBSv3, BSDecompressor_remaining + 4
|
||||
.set BSDecompressor_bitOffset, BSDecompressor_isBSv3 + 1
|
||||
.set BSDecompressor_blockIndex, BSDecompressor_bitOffset + 1
|
||||
.set BSDecompressor_coeffIndex, BSDecompressor_blockIndex + 1
|
||||
.set BSDecompressor_quantScale, BSDecompressor_coeffIndex + 1
|
||||
.set BSDecompressor_lastY, BSDecompressor_quantScale + 2
|
||||
.set BSDecompressor_lastCr, BSDecompressor_lastY + 2
|
||||
.set BSDecompressor_lastCb, BSDecompressor_lastCr + 2
|
||||
|
||||
.set BSHuffmanTable_ac0, 0
|
||||
.set BSHuffmanTable_ac2, BSHuffmanTable_ac0 + 2 * 2
|
||||
.set BSHuffmanTable_ac3, BSHuffmanTable_ac2 + 8 * 4
|
||||
.set BSHuffmanTable_ac4, BSHuffmanTable_ac3 + 64 * 4
|
||||
.set BSHuffmanTable_ac5, BSHuffmanTable_ac4 + 8 * 2
|
||||
.set BSHuffmanTable_ac7, BSHuffmanTable_ac5 + 8 * 2
|
||||
.set BSHuffmanTable_ac8, BSHuffmanTable_ac7 + 16 * 2
|
||||
.set BSHuffmanTable_ac9, BSHuffmanTable_ac8 + 32 * 2
|
||||
.set BSHuffmanTable_ac10, BSHuffmanTable_ac9 + 32 * 2
|
||||
.set BSHuffmanTable_ac11, BSHuffmanTable_ac10 + 32 * 2
|
||||
.set BSHuffmanTable_ac12, BSHuffmanTable_ac11 + 32 * 2
|
||||
.set BSHuffmanTable_dcValues, BSHuffmanTable_ac12 + 32 * 2
|
||||
.set BSHuffmanTable_dcLengths, BSHuffmanTable_dcValues + 128 * 1
|
||||
|
||||
## Constants
|
||||
|
||||
.set NO_ERROR, 0
|
||||
.set PARTIAL_DATA, 1
|
||||
.set DECODE_ERROR, 2
|
||||
|
||||
.set BLOCK_INDEX_Y3, 0
|
||||
.set BLOCK_INDEX_Y2, 1
|
||||
.set BLOCK_INDEX_Y1, 2
|
||||
.set BLOCK_INDEX_Y0, 3
|
||||
.set BLOCK_INDEX_CB, 4
|
||||
.set BLOCK_INDEX_CR, 5
|
||||
|
||||
.set MDEC_COMMAND, 0x3800
|
||||
.set MDEC_END_CODE, 0xfe00
|
||||
|
||||
.set BS_DC_END_CODE, 0x1ff
|
||||
.set BS_DC_BITMASK, 0x3ff
|
||||
|
||||
.set BS_DC_V2_LENGTH, 10
|
||||
.set BS_DC_V3_LENGTH, 7
|
||||
.set BS_DC_V3_END_LENGTH, 9
|
||||
|
||||
.set MAX_PREFIX_LENGTH, 11
|
||||
.set JUMP_CASE_LENGTH, 5 # (1 << 5) = 32 bytes = 8 instructions
|
||||
|
||||
## Local variables and macros
|
||||
|
||||
.set value, $v0
|
||||
.set length, $v1
|
||||
|
||||
.set this, $a0
|
||||
.set output, $a1
|
||||
.set outputLength, $a2
|
||||
.set input, $a3
|
||||
|
||||
.set temp, $t0
|
||||
.set bits, $t1
|
||||
.set nextBits, $t2
|
||||
.set remaining, $t3
|
||||
.set isBSv3, $t4
|
||||
.set bitOffset, $t5
|
||||
|
||||
.set blockIndex, $t6
|
||||
.set coeffIndex, $t7
|
||||
.set quantScale, $s0
|
||||
|
||||
.set lastY, $s1
|
||||
.set lastCr, $s2
|
||||
.set lastCb, $s3
|
||||
|
||||
.set huffmanTable, $t8
|
||||
.set acJumpPtr, $t9
|
||||
|
||||
.macro swapHalfwords reg
|
||||
# reg = uint32_t(reg << 16) | uint32_t(reg >> 16);
|
||||
srl temp, \reg, 16
|
||||
sll \reg, 16
|
||||
or \reg, temp
|
||||
.endm
|
||||
|
||||
.macro decodeBSv3DC lastValueReg, jumpTo
|
||||
# bits <<= temp;
|
||||
# bitOffset -= temp;
|
||||
sllv bits, bits, temp
|
||||
beqz length, 3f
|
||||
subu bitOffset, temp
|
||||
|
||||
0: # if (length) {
|
||||
# value = bits >> (32 - length);
|
||||
subu $at, length
|
||||
srlv value, bits, $at
|
||||
|
||||
# ((1 << length) - 1) below is computed as (0xffffffff >> (32 - length)) as
|
||||
# it takes fewer instructions.
|
||||
bltz bits, 2f
|
||||
li temp, -1
|
||||
|
||||
1:
|
||||
# if (!(bits >> 31)) value -= (1 << length) - 1;
|
||||
srlv temp, temp, $at
|
||||
subu value, temp
|
||||
|
||||
2:
|
||||
# lastValueReg += value * 4;
|
||||
# lastValueReg &= BS_DC_BITMASK;
|
||||
sll value, 2
|
||||
addu \lastValueReg, value
|
||||
andi \lastValueReg, BS_DC_BITMASK
|
||||
|
||||
3: # } else {
|
||||
# *output = lastY | quantScale;
|
||||
or temp, \lastValueReg, quantScale
|
||||
b \jumpTo
|
||||
sh temp, 0(output)
|
||||
.endm # }
|
||||
|
||||
.macro decodeFixedAC prefixLength, codeLength, discarded, tableImm, jumpTo
|
||||
# bitmask = (1 << codeLength) - 1;
|
||||
# value = (bits >> (32 - totalLength)) & bitmask;
|
||||
srl value, bits, 32 - (\prefixLength + \codeLength + 1)
|
||||
andi value, ((1 << \codeLength) - 1) << 1
|
||||
|
||||
# An extra shift (to multiply the code by the size of each table entry, 2
|
||||
# bytes) is avoided here by keeping one more bit from the sliding window
|
||||
# above, then masking it off.
|
||||
addu value, huffmanTable
|
||||
lhu value, \tableImm(value)
|
||||
|
||||
# bits <<= totalLength;
|
||||
# bitOffset -= totalLength + discarded;
|
||||
sll bits, \prefixLength + \codeLength
|
||||
addiu bitOffset, -(\prefixLength + \codeLength + \discarded)
|
||||
|
||||
# *output = tableImm[value];
|
||||
b \jumpTo
|
||||
sh value, 0(output)
|
||||
.endm
|
||||
|
||||
.macro decodeVariableAC prefixLength, codeLength, tableImm, jumpTo
|
||||
# Lookup tables with 32-bit entries are used to decode variable length
|
||||
# codes, with the upper 16 bits holding the length of the code.
|
||||
|
||||
# bitmask = (1 << codeLength) - 1;
|
||||
# value = (bits >> (32 - totalLength)) & bitmask;
|
||||
srl value, bits, 32 - (\prefixLength + \codeLength + 2)
|
||||
andi value, ((1 << \codeLength) - 1) << 2
|
||||
|
||||
# An extra shift (to multiply the code by the size of each table entry, 4
|
||||
# bytes) is avoided here by keeping two more bits from the sliding window
|
||||
# above, then masking it off.
|
||||
addu value, huffmanTable
|
||||
lw value, \tableImm(value)
|
||||
|
||||
# *output = tableImm[index] & 0xffff;
|
||||
# length = tableImm[index] >> 16;
|
||||
b \jumpTo
|
||||
sh value, 0(output)
|
||||
nop
|
||||
nop
|
||||
.endm
|
||||
|
||||
.macro decodeACEscape prefixLength, codeLength, discarded, jumpTo
|
||||
# shift = 32 - (prefixLength + codeLength);
|
||||
# value = bits >> shift;
|
||||
srl value, bits, 32 - (\prefixLength + \codeLength)
|
||||
|
||||
# bits <<= totalLength;
|
||||
# bitOffset -= totalLength + discarded;
|
||||
sll bits, \prefixLength + \codeLength
|
||||
addiu bitOffset, -(\prefixLength + \codeLength + \discarded)
|
||||
|
||||
# *output = value & 0xffff;
|
||||
b \jumpTo
|
||||
sh value, 0(output)
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endm
|
||||
|
||||
## MDEC bitstream decompressor
|
||||
|
||||
.section .text._bsDecompressorStart, "ax", @progbits
|
||||
.global _bsDecompressorStart
|
||||
.type _bsDecompressorStart, @function
|
||||
|
||||
_bsDecompressorStart:
|
||||
addiu $sp, -16
|
||||
sw $s0, 0($sp)
|
||||
sw $s1, 4($sp)
|
||||
sw $s2, 8($sp)
|
||||
sw $s3, 12($sp)
|
||||
|
||||
# header = (const BSHeader *) input;
|
||||
# bits = swapHalfwords(header->data[0]);
|
||||
# lastY = 0;
|
||||
lw bits, BSHeader_data0(input)
|
||||
li lastY, 0
|
||||
swapHalfwords bits
|
||||
|
||||
# nextBits = swapHalfwords(header->data[1]);
|
||||
# lastCr = 0;
|
||||
lw nextBits, BSHeader_data1(input)
|
||||
li lastCr, 0
|
||||
swapHalfwords nextBits
|
||||
|
||||
# remaining = header->outputLength * 2;
|
||||
# lastCb = 0;
|
||||
lhu remaining, BSHeader_outputLength(input)
|
||||
li lastCb, 0
|
||||
sll remaining, 1
|
||||
|
||||
# quantScale = (header->quantScale & 63) << 10;
|
||||
# bitOffset = 32;
|
||||
lw temp, BSHeader_quantScale(input)
|
||||
li bitOffset, 32
|
||||
andi quantScale, temp, 63
|
||||
sll quantScale, 10
|
||||
|
||||
# isBSv3 = !(header->version < 3);
|
||||
# blockIndex = BLOCK_INDEX_CR;
|
||||
# coeffIndex = 0;
|
||||
srl temp, 16
|
||||
sltiu isBSv3, temp, 3
|
||||
xori isBSv3, 1
|
||||
li blockIndex, BLOCK_INDEX_CR
|
||||
li coeffIndex, 0
|
||||
|
||||
# input = &(header->data[2]);
|
||||
j _bsDecompressorSkipContextLoad
|
||||
addiu input, 16
|
||||
|
||||
.section .text._bsDecompressorResume, "ax", @progbits
|
||||
.global _bsDecompressorResume
|
||||
.type _bsDecompressorResume, @function
|
||||
|
||||
_bsDecompressorResume:
|
||||
addiu $sp, -16
|
||||
sw $s0, 0($sp)
|
||||
sw $s1, 4($sp)
|
||||
sw $s2, 8($sp)
|
||||
sw $s3, 12($sp)
|
||||
|
||||
lw input, BSDecompressor_input (this)
|
||||
lw bits, BSDecompressor_bits (this)
|
||||
lw nextBits, BSDecompressor_nextBits (this)
|
||||
lw remaining, BSDecompressor_remaining (this)
|
||||
lb isBSv3, BSDecompressor_isBSv3 (this)
|
||||
lb bitOffset, BSDecompressor_bitOffset (this)
|
||||
lb blockIndex, BSDecompressor_blockIndex(this)
|
||||
lb coeffIndex, BSDecompressor_coeffIndex(this)
|
||||
lhu quantScale, BSDecompressor_quantScale(this)
|
||||
lh lastY, BSDecompressor_lastY (this)
|
||||
lh lastCr, BSDecompressor_lastCr (this)
|
||||
lh lastCb, BSDecompressor_lastCb (this)
|
||||
|
||||
_bsDecompressorSkipContextLoad:
|
||||
# if (outputLength <= 0) outputLength = 0x3fff0000;
|
||||
bgtz outputLength, .LoutputLengthValid
|
||||
addiu outputLength, -1
|
||||
|
||||
.LdefaultOutputLength:
|
||||
lui outputLength, 0x3fff
|
||||
|
||||
.LoutputLengthValid:
|
||||
# outputLength = min((outputLength - 1) * 2, remaining);
|
||||
# remaining -= outputLength;
|
||||
sll outputLength, 1
|
||||
subu remaining, outputLength
|
||||
bgez remaining, .LremainingValid
|
||||
lui temp, MDEC_COMMAND
|
||||
|
||||
.LadjustRemaining:
|
||||
addu outputLength, remaining
|
||||
li remaining, 0
|
||||
|
||||
.LremainingValid:
|
||||
# *(output++) = outputLength / 2;
|
||||
# *(output++) = MDEC_COMMAND;
|
||||
srl value, outputLength, 1
|
||||
or value, temp
|
||||
sw value, 0(output)
|
||||
|
||||
# huffmanTable = (const BSHuffmanTable *) CACHE_BASE;
|
||||
# acJumpPtr = &LacJumpArea;
|
||||
la huffmanTable, CACHE_BASE
|
||||
la acJumpPtr, .LacJumpArea
|
||||
|
||||
beqz outputLength, .Lbreak
|
||||
addiu output, 4
|
||||
|
||||
.LdecompressLoop: # while (outputLength) {
|
||||
# The first step to decompress each code is to determine whether it is a DC
|
||||
# or AC coefficient. At the same time the GTE is given the task of counting
|
||||
# the number of leading zeroes/ones in the code.
|
||||
mtc2 bits, GTE_LZCS
|
||||
|
||||
bnez coeffIndex, .LisACCoeff
|
||||
addiu coeffIndex, 1
|
||||
|
||||
bnez isBSv3, .LisBSv3DCCoeff
|
||||
li temp, BS_DC_END_CODE
|
||||
|
||||
.LisBSv2DCCoeff: # if (!coeffIndex && !isBSv3) {
|
||||
# The DC coefficient in v2 frames is a raw 10-bit value. Value 0x1ff is used
|
||||
# to signal the end of the bitstream.
|
||||
|
||||
# value = bits >> (32 - BS_DC_V2_LENGTH);
|
||||
# if (value == BS_DC_END_CODE) break;
|
||||
srl value, bits, 32 - BS_DC_V2_LENGTH
|
||||
beq value, temp, .Lbreak
|
||||
or value, quantScale
|
||||
|
||||
# bits <<= BS_DC_V2_LENGTH;
|
||||
# bitOffset -= BS_DC_V2_LENGTH;
|
||||
sll bits, BS_DC_V2_LENGTH
|
||||
addiu bitOffset, -BS_DC_V2_LENGTH
|
||||
|
||||
# *output = value | quantScale;
|
||||
b .Ldone
|
||||
sh value, 0(output)
|
||||
|
||||
.LisBSv3DCCoeff: # } else if (!coeffIndex && isBSv3) {
|
||||
# v3 DC coefficients are variable-length deltas prefixed with a Huffman code
|
||||
# indicating their length. Since the prefix code is up to 7 bits long, it
|
||||
# makes sense to decode it with a simple 128-byte lookup table rather than
|
||||
# using the GTE. The codes are different for luma and chroma blocks, so each
|
||||
# table entry contains the decoded length for both block types (packed as
|
||||
# two nibbles). Prefix 111111111 is used to signal the end of the bitstream.
|
||||
|
||||
# length = bits >> (32 - BS_DC_V3_END_LENGTH);
|
||||
# if (length == BS_DC_END_CODE) break;
|
||||
srl length, bits, 32 - BS_DC_V3_END_LENGTH
|
||||
beq length, temp, .Lbreak
|
||||
srl length, BS_DC_V3_END_LENGTH - BS_DC_V3_LENGTH
|
||||
|
||||
# length >>= BS_DC_V3_END_LENGTH - BS_DC_V3_LENGTH;
|
||||
# length = huffmanTable->dcValues[length];
|
||||
addu length, huffmanTable
|
||||
|
||||
addiu $at, blockIndex, -BLOCK_INDEX_CB
|
||||
bltz $at, .LisY
|
||||
lbu length, BSHuffmanTable_dcValues(length)
|
||||
|
||||
# if (blockIndex >= BLOCK_INDEX_CB)
|
||||
beqz $at, .LisCb
|
||||
andi length, 15
|
||||
|
||||
.LisCr: # if (blockIndex > BLOCK_INDEX_CB) {
|
||||
# length &= 15;
|
||||
# temp = huffmanTable->dcLengths[length] & 15;
|
||||
addu temp, length, huffmanTable
|
||||
lbu temp, BSHuffmanTable_dcLengths(temp)
|
||||
li $at, 32
|
||||
|
||||
# decodeBSv3DC(lastCb);
|
||||
# bits <<= length;
|
||||
# bitOffset -= length;
|
||||
andi temp, 15
|
||||
decodeBSv3DC lastCr, .LconsumeBits
|
||||
|
||||
.LisCb: # } else if (block_index == BLOCK_INDEX_CB) {
|
||||
# length &= 15;
|
||||
# temp = huffmanTable->dcLengths[length] & 15;
|
||||
addu temp, length, huffmanTable
|
||||
lbu temp, BSHuffmanTable_dcLengths(temp)
|
||||
li $at, 32
|
||||
|
||||
# decodeBSv3DC(lastCb);
|
||||
# bits <<= length;
|
||||
# bitOffset -= length;
|
||||
andi temp, 15
|
||||
decodeBSv3DC lastCb, .LconsumeBits
|
||||
|
||||
.LisY: # } else {
|
||||
# length >>= 4;
|
||||
# temp = huffmanTable->dcLengths[length] >> 4;
|
||||
nop
|
||||
srl length, 4
|
||||
|
||||
addu temp, length, huffmanTable
|
||||
lbu temp, BSHuffmanTable_dcLengths(temp)
|
||||
li $at, 32
|
||||
|
||||
# decodeBSv3DC(lastCb);
|
||||
# bits <<= length;
|
||||
# bitOffset -= length;
|
||||
srl temp, 4
|
||||
decodeBSv3DC lastY, .LconsumeBits
|
||||
|
||||
.LisACCoeff: # } } else {
|
||||
# Check whether the code starts with 10 or 11; if not, retrieve the leading
|
||||
# zero count from the GTE, validate it and use it as an index into the jump
|
||||
# area. Each case is 8 instructions long and handles decoding a specific
|
||||
# Huffman prefix.
|
||||
|
||||
# temp = countLeadingZeroes(temp);
|
||||
mfc2 temp, GTE_LZCR
|
||||
|
||||
bltz bits, .Lac1
|
||||
addiu $at, temp, -MAX_PREFIX_LENGTH
|
||||
|
||||
bgtz $at, .LacInvalid
|
||||
sll temp, JUMP_CASE_LENGTH
|
||||
|
||||
.Lac0: # if ((prefix <= MAX_PREFIX_LENGTH) && !(bits >> 31)) {
|
||||
# goto &acJumpArea[prefix << JUMP_CASE_LENGTH];
|
||||
addu temp, acJumpPtr
|
||||
jr temp
|
||||
nop
|
||||
|
||||
.LacInvalid: # } else if (prefix > MAX_PREFIX_LENGTH) {
|
||||
# return DECODE_ERROR;
|
||||
b .Lreturn
|
||||
li $v0, DECODE_ERROR
|
||||
|
||||
.Lac1: # } else {
|
||||
# bits <<= 1;
|
||||
# if (bits >> 31) goto &acJumpArea[0];
|
||||
sll bits, 1
|
||||
bltz bits, .LacJumpArea
|
||||
li temp, MDEC_END_CODE
|
||||
|
||||
.Lac10: # else {
|
||||
# Prefix 10 marks the end of a block. Note that the 10/11 prefix check above
|
||||
# shifts the window by one bit *without* updating the bit offset.
|
||||
|
||||
# bits <<= 1;
|
||||
# bitOffset -= 2;
|
||||
sll bits, 1
|
||||
addiu bitOffset, -2
|
||||
|
||||
# *output = MDEC_END_CODE;
|
||||
sh temp, 0(output)
|
||||
|
||||
# coeffIndex = 0;
|
||||
# if (--blockIndex < BLOCK_INDEX_Y3) blockIndex = BLOCK_INDEX_CR;
|
||||
addiu blockIndex, -1
|
||||
bgez blockIndex, .Ldone
|
||||
li coeffIndex, 0
|
||||
|
||||
.LresetBlockIndex:
|
||||
b .Ldone
|
||||
li blockIndex, BLOCK_INDEX_CR
|
||||
|
||||
.LacJumpArea: # } }
|
||||
decodeFixedAC 1, 1, 1, BSHuffmanTable_ac0, .Ldone # (1)1x
|
||||
decodeVariableAC 2, 3, BSHuffmanTable_ac2, .LconsumeAC # 01xxx
|
||||
decodeVariableAC 3, 6, BSHuffmanTable_ac3, .LconsumeAC # 001xxxxxx
|
||||
decodeFixedAC 4, 3, 0, BSHuffmanTable_ac4, .Ldone # 0001xxx
|
||||
decodeFixedAC 5, 3, 0, BSHuffmanTable_ac5, .Ldone # 00001xxx
|
||||
decodeACEscape 6, 16, 0, .Ldone # 000001xxxxxxxxxxxxxxxx
|
||||
decodeFixedAC 7, 4, 0, BSHuffmanTable_ac7, .Ldone # 0000001xxxx
|
||||
decodeFixedAC 8, 5, 0, BSHuffmanTable_ac8, .Ldone # 00000001xxxxx
|
||||
decodeFixedAC 9, 5, 0, BSHuffmanTable_ac9, .Ldone # 000000001xxxxx
|
||||
decodeFixedAC 10, 5, 0, BSHuffmanTable_ac10, .Ldone # 0000000001xxxxx
|
||||
decodeFixedAC 11, 5, 0, BSHuffmanTable_ac11, .Ldone # 00000000001xxxxx
|
||||
decodeFixedAC 12, 5, 0, BSHuffmanTable_ac12, .Ldone # 000000000001xxxxx
|
||||
|
||||
.LconsumeAC:
|
||||
srl length, value, 16
|
||||
|
||||
.LconsumeBits:
|
||||
sllv bits, bits, length
|
||||
subu bitOffset, length
|
||||
|
||||
.Ldone: # }
|
||||
# Update the bits. This makes sure the next iteration of the loop will be
|
||||
# able to read up to 32 bits from the bitstream.
|
||||
|
||||
# coeffIndex++;
|
||||
# outputLength--;
|
||||
bgez bitOffset, .LskipFeeding
|
||||
addiu outputLength, -1
|
||||
|
||||
.LfeedBitstream: # if (bitOffset < 0) {
|
||||
# bits = nextBits << (-bitOffset);
|
||||
subu temp, $0, bitOffset
|
||||
sllv bits, nextBits, temp
|
||||
|
||||
# nextBits = swapHalfwords(*(input++));
|
||||
# bitOffset += 32;
|
||||
lw nextBits, 0(input)
|
||||
addiu bitOffset, 32
|
||||
swapHalfwords nextBits
|
||||
addiu input, 4
|
||||
|
||||
.LskipFeeding: # }
|
||||
# bits |= nextBits >> bitOffset;
|
||||
# output++;
|
||||
srlv temp, nextBits, bitOffset
|
||||
or bits, temp
|
||||
bnez outputLength, .LdecompressLoop
|
||||
addiu output, 2
|
||||
|
||||
.Lbreak: # }
|
||||
beqz remaining, .LpadOutput
|
||||
li temp, MDEC_END_CODE
|
||||
|
||||
.LsaveContext: # if (remaining) {
|
||||
sw input, BSDecompressor_input (this)
|
||||
sw bits, BSDecompressor_bits (this)
|
||||
sw nextBits, BSDecompressor_nextBits (this)
|
||||
sw remaining, BSDecompressor_remaining (this)
|
||||
sb bitOffset, BSDecompressor_bitOffset (this)
|
||||
sb blockIndex, BSDecompressor_blockIndex(this)
|
||||
sb coeffIndex, BSDecompressor_coeffIndex(this)
|
||||
sh lastY, BSDecompressor_lastY (this)
|
||||
sh lastCr, BSDecompressor_lastCr (this)
|
||||
sh lastCb, BSDecompressor_lastCb (this)
|
||||
|
||||
# return PARTIAL_DATA;
|
||||
b .Lreturn
|
||||
li $v0, PARTIAL_DATA
|
||||
|
||||
.LpadOutput: # } else {
|
||||
# for (; outputLength; outputLength--) *(output++) = MDEC_END_CODE;
|
||||
# return NO_ERROR;
|
||||
beqz outputLength, .Lreturn
|
||||
li $v0, NO_ERROR
|
||||
|
||||
.LpadOutputLoop:
|
||||
sh temp, 0(output)
|
||||
addiu outputLength, -1
|
||||
bnez outputLength, .LpadOutputLoop
|
||||
addiu output, 2
|
||||
|
||||
.Lreturn: # }
|
||||
lw $s0, 0($sp)
|
||||
lw $s1, 4($sp)
|
||||
lw $s2, 8($sp)
|
||||
lw $s3, 12($sp)
|
||||
|
||||
jr $ra
|
||||
addiu $sp, 16
|
@ -89,7 +89,6 @@ public:
|
||||
|
||||
// Note that all offsets must be multiples of 2, as writes are done in
|
||||
// halfwords.
|
||||
virtual ~Driver(void) {}
|
||||
virtual void write(uint32_t offset, uint16_t value) {}
|
||||
virtual void eraseSector(uint32_t offset) {}
|
||||
virtual void eraseChip(uint32_t offset) {}
|
||||
|
@ -127,6 +127,9 @@ void IDEDevice::_writePIO(const void *data, size_t length) const {
|
||||
bool IDEDevice::_readDMA(void *data, size_t length) const {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT))
|
||||
return false;
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
@ -141,6 +144,9 @@ bool IDEDevice::_readDMA(void *data, size_t length) const {
|
||||
bool IDEDevice::_writeDMA(const void *data, size_t length) const {
|
||||
util::assertAligned<uint32_t>(data);
|
||||
|
||||
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT))
|
||||
return false;
|
||||
|
||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
||||
DMA_CHCR(DMA_PIO) = 0
|
||||
|
@ -206,7 +206,6 @@ public:
|
||||
inline Device(uint8_t flags = 0)
|
||||
: type(NONE), flags(flags), sectorLength(0), capacity(0) {}
|
||||
|
||||
virtual ~Device(void) {}
|
||||
virtual DeviceError enumerate(void) { return UNSUPPORTED_OP; }
|
||||
virtual DeviceError poll(void) { return UNSUPPORTED_OP; }
|
||||
virtual void handleInterrupt(void) {}
|
||||
|
@ -111,6 +111,46 @@ size_t encodeBase41(char *output, const uint8_t *input, size_t length) {
|
||||
|
||||
/* UTF-8 parser */
|
||||
|
||||
#if 0
|
||||
#define L1(length) (length)
|
||||
#define L2(length) L1(length), L1(length)
|
||||
#define L4(length) L2(length), L2(length)
|
||||
#define L8(length) L4(length), L4(length)
|
||||
#define L16(length) L8(length), L8(length)
|
||||
|
||||
static const uint8_t _START_BYTE_LENGTHS[]{
|
||||
L16(1), // 0xxxx--- (1 byte)
|
||||
L8 (0), // 10xxx--- (invalid)
|
||||
L4 (2), // 110xx--- (2 bytes)
|
||||
L2 (3), // 1110x--- (3 bytes)
|
||||
L1 (4), // 11110--- (4 bytes)
|
||||
L1 (0) // 11111--- (invalid)
|
||||
};
|
||||
|
||||
static const uint8_t _START_BYTE_MASKS[]{
|
||||
0x00,
|
||||
0x7f, // 0xxxxxxx (1 byte)
|
||||
0x1f, // 110xxxxx (2 bytes)
|
||||
0x0f, // 1110xxxx (3 bytes)
|
||||
0x07 // 11110xxx (4 bytes)
|
||||
};
|
||||
|
||||
UTF8Character parseUTF8Character(const char *ch) {
|
||||
auto start = uint8_t(*(ch++));
|
||||
auto length = _START_BYTE_LENGTHS[start >> 3];
|
||||
auto mask = _START_BYTE_MASKS[length];
|
||||
|
||||
auto codePoint = UTF8CodePoint(start & mask);
|
||||
|
||||
for (size_t i = length - 1; i; i--) {
|
||||
codePoint <<= 6;
|
||||
codePoint |= *(ch++) & 0x3f;
|
||||
}
|
||||
|
||||
return { codePoint, length };
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t getUTF8StringLength(const char *str) {
|
||||
for (size_t length = 0;; length++) {
|
||||
auto value = parseUTF8Character(str);
|
||||
|
@ -43,17 +43,19 @@ public:
|
||||
size_t length;
|
||||
};
|
||||
|
||||
#if 0
|
||||
UTF8Character parseUTF8Character(const char *ch);
|
||||
#else
|
||||
extern "C" uint64_t _parseUTF8Character(const char *ch);
|
||||
size_t getUTF8StringLength(const char *str);
|
||||
|
||||
static inline UTF8Character parseUTF8Character(const char *ch) {
|
||||
auto values = _parseUTF8Character(ch);
|
||||
|
||||
return {
|
||||
.codePoint = UTF8CodePoint(values),
|
||||
.length = size_t(values >> 32)
|
||||
};
|
||||
return { UTF8CodePoint(values), size_t(values >> 32) };
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t getUTF8StringLength(const char *str);
|
||||
|
||||
/* LZ4 decompressor */
|
||||
|
||||
|
@ -50,14 +50,16 @@ template<typename T, typename E> T Tween<T, E>::getValue(int time) const {
|
||||
) / TWEEN_UNIT;
|
||||
}
|
||||
|
||||
// NOTE: T should always be a signed type, as the delta will be negative when
|
||||
// interpolating from a higher value to a lower one.
|
||||
template class Tween<int, LinearEasing>;
|
||||
template class Tween<int, QuadInEasing>;
|
||||
template class Tween<int, QuadOutEasing>;
|
||||
template class Tween<uint16_t, LinearEasing>;
|
||||
template class Tween<uint16_t, QuadInEasing>;
|
||||
template class Tween<uint16_t, QuadOutEasing>;
|
||||
template class Tween<uint32_t, LinearEasing>;
|
||||
template class Tween<uint32_t, QuadInEasing>;
|
||||
template class Tween<uint32_t, QuadOutEasing>;
|
||||
template class Tween<int8_t, LinearEasing>;
|
||||
template class Tween<int8_t, QuadInEasing>;
|
||||
template class Tween<int8_t, QuadOutEasing>;
|
||||
template class Tween<int16_t, LinearEasing>;
|
||||
template class Tween<int16_t, QuadInEasing>;
|
||||
template class Tween<int16_t, QuadOutEasing>;
|
||||
|
||||
}
|
||||
|
@ -272,10 +272,10 @@ void App::_loadResources(void) {
|
||||
res.loadTIM(_splashOverlay.image, "assets/textures/splash.tim");
|
||||
res.loadData(_stringTable, "assets/lang/en.lang");
|
||||
|
||||
fs::currentSPUOffset = spu::DUMMY_BLOCK_END;
|
||||
uint32_t spuOffset = spu::DUMMY_BLOCK_END;
|
||||
|
||||
for (int i = 0; i < ui::NUM_UI_SOUNDS; i++)
|
||||
res.loadVAG(_ctx.sounds[i], _UI_SOUND_PATHS[i]);
|
||||
res.loadVAG(_ctx.sounds[i], spuOffset, _UI_SOUND_PATHS[i]);
|
||||
}
|
||||
|
||||
bool App::_createDataDirectory(void) {
|
||||
|
@ -267,15 +267,15 @@ bool App::_cartRestoreWorker(void) {
|
||||
_workerStatus.update(0, 3, WSTR("App.cartRestoreWorker.init"));
|
||||
|
||||
const char *path = _fileBrowserScreen.selectedPath;
|
||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
auto file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
|
||||
cart::CartDump newDump;
|
||||
|
||||
if (_file) {
|
||||
auto length = _file->read(&newDump, sizeof(newDump));
|
||||
if (file) {
|
||||
auto length = file->read(&newDump, sizeof(newDump));
|
||||
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
|
||||
if (length < (sizeof(newDump) - sizeof(newDump.data)))
|
||||
goto _fileError;
|
||||
|
@ -310,7 +310,7 @@ void ResolutionScreen::update(ui::Context &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint16_t _LOOP_FADE_IN_VOLUME = spu::MAX_VOLUME / 2;
|
||||
static constexpr int _LOOP_FADE_IN_VOLUME = spu::MAX_VOLUME / 2;
|
||||
static constexpr int _LOOP_FADE_IN_TIME = 30;
|
||||
|
||||
void AboutScreen::show(ui::Context &ctx, bool goBack) {
|
||||
|
@ -65,7 +65,7 @@ private:
|
||||
util::Data _text;
|
||||
spu::Channel _loopChannel;
|
||||
|
||||
util::Tween<uint16_t, util::QuadInEasing> _loopVolume;
|
||||
util::Tween<int, util::QuadInEasing> _loopVolume;
|
||||
|
||||
public:
|
||||
void show(ui::Context &ctx, bool goBack = false);
|
||||
|
@ -151,12 +151,12 @@ bool App::_executableWorker(void) {
|
||||
} else {
|
||||
__builtin_memset(header.magic, 0, sizeof(header.magic));
|
||||
|
||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
auto file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
|
||||
if (_file) {
|
||||
_file->read(&header, sizeof(header));
|
||||
_file->close();
|
||||
delete _file;
|
||||
if (file) {
|
||||
file->read(&header, sizeof(header));
|
||||
file->close();
|
||||
delete file;
|
||||
}
|
||||
|
||||
if (!header.validateMagic()) {
|
||||
|
@ -221,7 +221,7 @@ void FilePickerScreen::show(ui::Context &ctx, bool goBack) {
|
||||
_listLength = 0;
|
||||
|
||||
#ifdef ENABLE_PCDRV
|
||||
_addDevice(nullptr, APP->_fileIO.host, "host:");
|
||||
_addDevice(nullptr, &(APP->_fileIO.host), "host:");
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < util::countOf(APP->_fileIO.ideDevices); i++)
|
||||
|
@ -145,11 +145,11 @@ bool App::_romDumpWorker(void) {
|
||||
|
||||
snprintf(filePath, sizeof(filePath), entry.path, dirPath);
|
||||
|
||||
auto _file = _fileIO.vfs.openFile(
|
||||
auto file = _fileIO.vfs.openFile(
|
||||
filePath, fs::WRITE | fs::ALLOW_CREATE
|
||||
);
|
||||
|
||||
if (!_file)
|
||||
if (!file)
|
||||
goto _fileError;
|
||||
|
||||
util::Data buffer;
|
||||
@ -161,10 +161,10 @@ bool App::_romDumpWorker(void) {
|
||||
_workerStatus.update(i, numChunks, WSTRH(entry.dumpPrompt));
|
||||
entry.region.read(buffer.ptr, offset, chunkLength);
|
||||
|
||||
if (_file->write(buffer.ptr, chunkLength) < chunkLength) {
|
||||
if (file->write(buffer.ptr, chunkLength) < chunkLength) {
|
||||
buffer.destroy();
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
|
||||
goto _fileError;
|
||||
}
|
||||
@ -173,8 +173,8 @@ bool App::_romDumpWorker(void) {
|
||||
}
|
||||
|
||||
buffer.destroy();
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
|
||||
LOG_APP("%s saved", filePath);
|
||||
}
|
||||
@ -201,9 +201,9 @@ bool App::_romRestoreWorker(void) {
|
||||
_workerStatus.update(0, 1, WSTR("App.romRestoreWorker.init"));
|
||||
|
||||
const char *path = _fileBrowserScreen.selectedPath;
|
||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
auto file = _fileIO.vfs.openFile(path, fs::READ);
|
||||
|
||||
if (!_file) {
|
||||
if (!file) {
|
||||
_messageScreen.setMessage(
|
||||
MESSAGE_ERROR, WSTR("App.romRestoreWorker.fileError"), path
|
||||
);
|
||||
@ -243,8 +243,8 @@ bool App::_romRestoreWorker(void) {
|
||||
size_t j = numChips; j > 0; j--, bufferPtr += maxChunkLength,
|
||||
offset += chipLength
|
||||
) {
|
||||
_file->seek(offset);
|
||||
auto length = _file->read(bufferPtr, maxChunkLength);
|
||||
file->seek(offset);
|
||||
auto length = file->read(bufferPtr, maxChunkLength);
|
||||
|
||||
// Data is written 16 bits at a time, so the chunk must be padded to
|
||||
// an even number of bytes.
|
||||
@ -301,8 +301,8 @@ bool App::_romRestoreWorker(void) {
|
||||
|
||||
buffers.destroy();
|
||||
chunkLengths.destroy();
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
delete driver;
|
||||
|
||||
_messageScreen.setMessage(
|
||||
@ -316,14 +316,14 @@ bool App::_romRestoreWorker(void) {
|
||||
|
||||
util::Hash message;
|
||||
|
||||
message = (_file->size > regionLength)
|
||||
message = (file->size > regionLength)
|
||||
? "App.romRestoreWorker.overflow"_h
|
||||
: "App.romRestoreWorker.success"_h;
|
||||
|
||||
buffers.destroy();
|
||||
chunkLengths.destroy();
|
||||
_file->close();
|
||||
delete _file;
|
||||
file->close();
|
||||
delete file;
|
||||
delete driver;
|
||||
|
||||
_messageScreen.setMessage(MESSAGE_SUCCESS, WSTRH(message), bytesWritten);
|
||||
|
@ -133,7 +133,6 @@ public:
|
||||
inline CartParser(CartDump &dump, uint8_t flags = 0)
|
||||
: _dump(dump), flags(flags) {}
|
||||
|
||||
virtual ~CartParser(void) {}
|
||||
virtual size_t getCode(char *output) const { return 0; }
|
||||
virtual void setCode(const char *input) {}
|
||||
virtual size_t getRegion(char *output) const { return 0; }
|
||||
@ -219,7 +218,6 @@ public:
|
||||
inline ROMHeaderParser(ROMHeaderDump &dump, uint8_t flags = 0)
|
||||
: _dump(dump), flags(flags) {}
|
||||
|
||||
virtual ~ROMHeaderParser(void) {}
|
||||
virtual size_t getCode(char *output) const { return 0; }
|
||||
virtual void setCode(const char *input) {}
|
||||
virtual size_t getRegion(char *output) const { return 0; }
|
||||
|
@ -47,7 +47,6 @@ public:
|
||||
inline Driver(CartDump &dump)
|
||||
: _dump(dump) {}
|
||||
|
||||
virtual ~Driver(void) {}
|
||||
virtual DriverError readSystemID(void) { return UNSUPPORTED_OP; }
|
||||
virtual DriverError readCartID(void) { return UNSUPPORTED_OP; }
|
||||
virtual DriverError readPublicData(void) { return UNSUPPORTED_OP; }
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "common/gpu.hpp"
|
||||
#include "common/io.hpp"
|
||||
#include "common/ioboard.hpp"
|
||||
#include "common/mdec.hpp"
|
||||
#include "common/spu.hpp"
|
||||
#include "main/app/app.hpp"
|
||||
#include "main/uibase.hpp"
|
||||
@ -35,6 +36,7 @@ int main(int argc, const char **argv) {
|
||||
installExceptionHandler();
|
||||
gpu::init();
|
||||
spu::init();
|
||||
mdec::init();
|
||||
io::init();
|
||||
util::initZipCRC32();
|
||||
|
||||
|
@ -369,6 +369,26 @@ typedef enum {
|
||||
/* MDEC */
|
||||
|
||||
typedef enum {
|
||||
MDEC_CMD_NOP = 0 << 29,
|
||||
MDEC_CMD_DECODE = 1 << 29,
|
||||
MDEC_CMD_SET_QUANT_TABLE = 2 << 29,
|
||||
MDEC_CMD_SET_IDCT_TABLE = 3 << 29
|
||||
} MDECCommand;
|
||||
|
||||
typedef enum {
|
||||
MDEC_CMD_FLAG_LENGTH_BITMASK = 0xffff << 0, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_USE_CHROMA = 1 << 0, // MDEC_CMD_SET_QUANT_TABLE
|
||||
MDEC_CMD_FLAG_SIGNED = 1 << 25, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_16BPP_MASK = 1 << 26, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_FORMAT_BITMASK = 3 << 27, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_FORMAT_4BPP = 0 << 27, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_FORMAT_8BPP = 1 << 27, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_FORMAT_24BPP = 2 << 27, // MDEC_CMD_DECODE
|
||||
MDEC_CMD_FLAG_FORMAT_16BPP = 3 << 27 // MDEC_CMD_DECODE
|
||||
} MDECCommandFlag;
|
||||
|
||||
typedef enum {
|
||||
MDEC_STAT_LENGTH_BITMASK = 0xffff << 0,
|
||||
MDEC_STAT_BLOCK_BITMASK = 7 << 16,
|
||||
MDEC_STAT_BLOCK_Y0 = 0 << 16,
|
||||
MDEC_STAT_BLOCK_Y1 = 1 << 16,
|
||||
@ -376,6 +396,13 @@ typedef enum {
|
||||
MDEC_STAT_BLOCK_Y3 = 3 << 16,
|
||||
MDEC_STAT_BLOCK_CR = 4 << 16,
|
||||
MDEC_STAT_BLOCK_CB = 5 << 16,
|
||||
MDEC_STAT_16BPP_MASK = 1 << 23,
|
||||
MDEC_STAT_SIGNED = 1 << 24,
|
||||
MDEC_STAT_FORMAT_BITMASK = 3 << 25,
|
||||
MDEC_STAT_FORMAT_4BPP = 0 << 25,
|
||||
MDEC_STAT_FORMAT_8BPP = 1 << 25,
|
||||
MDEC_STAT_FORMAT_24BPP = 2 << 25,
|
||||
MDEC_STAT_FORMAT_16BPP = 3 << 25,
|
||||
MDEC_STAT_DREQ_OUT = 1 << 27,
|
||||
MDEC_STAT_DREQ_IN = 1 << 28,
|
||||
MDEC_STAT_BUSY = 1 << 29,
|
||||
|
Loading…
x
Reference in New Issue
Block a user