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"
|
HOMEPAGE_URL "https://github.com/spicyjpeg/573in1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
include(cmake/options.cmake)
|
||||||
|
|
||||||
|
## Source files
|
||||||
|
|
||||||
set(
|
set(
|
||||||
RELEASE_INFO "${PROJECT_NAME} ${PROJECT_VERSION} - (C) 2022-2024 spicyjpeg"
|
ps1Sources
|
||||||
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
|
|
||||||
#src/libc/crt0.c
|
#src/libc/crt0.c
|
||||||
src/libc/cxxsupport.cpp
|
src/libc/cxxsupport.cpp
|
||||||
src/libc/malloc.c
|
src/libc/malloc.c
|
||||||
@ -66,75 +42,61 @@ add_library(
|
|||||||
src/ps1/system.s
|
src/ps1/system.s
|
||||||
src/ps1/unhandledexc.c
|
src/ps1/unhandledexc.c
|
||||||
)
|
)
|
||||||
target_include_directories(
|
set(
|
||||||
common PUBLIC
|
vendorSources
|
||||||
src
|
src/vendor/ff.c
|
||||||
src/libc
|
src/vendor/ffunicode.c
|
||||||
|
src/vendor/miniz.c
|
||||||
|
src/vendor/printf.c
|
||||||
|
src/vendor/qrcodegen.c
|
||||||
)
|
)
|
||||||
target_compile_options(
|
|
||||||
common PUBLIC
|
set(
|
||||||
-Wall
|
commonSources
|
||||||
-Wextra
|
src/common/args.cpp
|
||||||
-Wno-unused-parameter
|
src/common/gpu.cpp
|
||||||
$<$<COMPILE_LANGUAGE:CXX>:
|
src/common/gpufont.cpp
|
||||||
-Wno-pmf-conversions
|
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(
|
set(
|
||||||
common PUBLIC
|
fsSources
|
||||||
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
|
|
||||||
src/common/fs/fat.cpp
|
src/common/fs/fat.cpp
|
||||||
src/common/fs/file.cpp
|
src/common/fs/file.cpp
|
||||||
src/common/fs/iso9660.cpp
|
src/common/fs/iso9660.cpp
|
||||||
src/common/fs/misc.cpp
|
src/common/fs/misc.cpp
|
||||||
src/common/fs/zip.cpp
|
src/common/fs/zip.cpp
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
storageSources
|
||||||
src/common/storage/ata.cpp
|
src/common/storage/ata.cpp
|
||||||
src/common/storage/atapi.cpp
|
src/common/storage/atapi.cpp
|
||||||
src/common/storage/device.cpp
|
src/common/storage/device.cpp
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
utilSources
|
||||||
src/common/util/hash.cpp
|
src/common/util/hash.cpp
|
||||||
src/common/util/log.cpp
|
src/common/util/log.cpp
|
||||||
src/common/util/misc.cpp
|
src/common/util/misc.cpp
|
||||||
src/common/util/string.cpp
|
src/common/util/string.cpp
|
||||||
src/common/util/string.s
|
src/common/util/string.s
|
||||||
src/common/util/tween.cpp
|
src/common/util/tween.cpp
|
||||||
src/common/args.cpp
|
)
|
||||||
src/common/gpu.cpp
|
|
||||||
src/common/gpufont.cpp
|
set(
|
||||||
src/common/io.cpp
|
mainSources
|
||||||
src/common/ioboard.cpp
|
${ps1Sources}
|
||||||
src/common/pad.cpp
|
${vendorSources}
|
||||||
src/common/rom.cpp
|
${commonSources}
|
||||||
src/common/romdrivers.cpp
|
${fsSources}
|
||||||
src/common/spu.cpp
|
${storageSources}
|
||||||
|
${utilSources}
|
||||||
src/libc/crt0.c
|
src/libc/crt0.c
|
||||||
src/main/app/app.cpp
|
src/main/app/app.cpp
|
||||||
src/main/app/cartactions.cpp
|
src/main/app/cartactions.cpp
|
||||||
@ -155,82 +117,109 @@ addPS1Executable(
|
|||||||
src/main/uibase.cpp
|
src/main/uibase.cpp
|
||||||
src/main/uicommon.cpp
|
src/main/uicommon.cpp
|
||||||
src/main/uimodals.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(
|
set(
|
||||||
main PRIVATE
|
launcherSources
|
||||||
# Logging options
|
${ps1Sources}
|
||||||
ENABLE_APP_LOGGING=1
|
${storageSources}
|
||||||
ENABLE_CART_IO_LOGGING=1
|
${utilSources}
|
||||||
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
|
|
||||||
src/common/args.cpp
|
src/common/args.cpp
|
||||||
src/common/io.cpp
|
src/common/io.cpp
|
||||||
src/launcher/exchandler.s
|
src/launcher/exchandler.s
|
||||||
src/launcher/main.cpp
|
src/launcher/main.cpp
|
||||||
src/libc/crt0.c
|
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()
|
endfunction()
|
||||||
|
|
||||||
# IMPORTANT: these addresses assume the launcher's total size (including code,
|
# IMPORTANT: these addresses assume the boot executable's size (including code,
|
||||||
# heap and stack allocations, but excluding the executable header) is <12 KB
|
# heap and stack allocations as well as the resource archive, but excluding the
|
||||||
# (0x3000 bytes).
|
# executable header) is 576 KB (0x90000 bytes) or less, and that each launcher's
|
||||||
addLauncher(801fd000 801ffff0)
|
# size is 12 KB (0x3000 bytes) or less.
|
||||||
addLauncher(803fd000 803ffff0)
|
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/*)
|
file(GLOB_RECURSE assetList RELATIVE "${PROJECT_SOURCE_DIR}" assets/*)
|
||||||
|
|
||||||
configure_file(assets/about.txt about.txt NEWLINE_STYLE LF)
|
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)
|
configure_file(${resourceName}.json ${resourceName}.json ESCAPE_QUOTES)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
@ -250,23 +239,17 @@ function(addBootStub name resourceName)
|
|||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
|
|
||||||
addPS1Executable(
|
addPS1Executable(${name} 80010000 0)
|
||||||
${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
|
|
||||||
)
|
|
||||||
addBinaryFile(
|
addBinaryFile(
|
||||||
${name} _resourceArchive _resourceArchiveLength
|
${name} _resourceArchive _resourceArchiveLength
|
||||||
"${PROJECT_BINARY_DIR}/${resourceName}.zip"
|
"${PROJECT_BINARY_DIR}/${resourceName}.zip"
|
||||||
)
|
)
|
||||||
target_link_libraries(${name} PRIVATE bootFlags)
|
|
||||||
|
target_link_libraries(${name} PUBLIC bootStub)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
addBootStub("${RELEASE_NAME}" resources)
|
addBuildVariant("${RELEASE_NAME}" resources)
|
||||||
addBootStub("${RELEASE_NAME}-tiny" resourcestiny)
|
addBuildVariant("${RELEASE_NAME}-tiny" resourcestiny)
|
||||||
|
|
||||||
## CD-ROM image
|
## 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);
|
util::hexValueToString(&_lengthArg[16], _resourceArchiveLength, 8);
|
||||||
loader.addArgument(_lengthArg);
|
loader.addArgument(_lengthArg);
|
||||||
|
|
||||||
#ifdef ENABLE_ARGV
|
#ifdef ENABLE_ARGV_PARSER
|
||||||
for (; argc > 0; argc--) {
|
for (; argc > 0; argc--) {
|
||||||
if (!loader.copyArgument(*(argv++)))
|
if (!loader.copyArgument(*(argv++)))
|
||||||
break;
|
break;
|
||||||
|
@ -246,17 +246,17 @@ bool FATProvider::createDirectory(const char *path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
File *FATProvider::openFile(const char *path, uint32_t flags) {
|
File *FATProvider::openFile(const char *path, uint32_t flags) {
|
||||||
auto _file = new FATFile();
|
auto file = new FATFile();
|
||||||
auto error = f_open(&_fs, &(_file->_fd), path, uint8_t(flags));
|
auto error = f_open(&_fs, &(file->_fd), path, uint8_t(flags));
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path);
|
LOG_FS("%s: %s", _FATFS_ERROR_NAMES[error], path);
|
||||||
delete _file;
|
delete file;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
_file->size = f_size(&(_file->_fd));
|
file->size = f_size(&(file->_fd));
|
||||||
return _file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FatFs library API glue */
|
/* FatFs library API glue */
|
||||||
|
@ -52,56 +52,54 @@ Directory::~Directory(void) {
|
|||||||
|
|
||||||
/* Base file and asset provider classes */
|
/* Base file and asset provider classes */
|
||||||
|
|
||||||
uint32_t currentSPUOffset = spu::DUMMY_BLOCK_END;
|
|
||||||
|
|
||||||
Provider::~Provider(void) {
|
Provider::~Provider(void) {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Provider::loadData(util::Data &output, const char *path) {
|
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;
|
return 0;
|
||||||
|
|
||||||
assert(_file->size <= SIZE_MAX);
|
assert(file->size <= SIZE_MAX);
|
||||||
if (!output.allocate(size_t(_file->size))) {
|
if (!output.allocate(size_t(file->size))) {
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t actualLength = _file->read(output.ptr, output.length);
|
size_t actualLength = file->read(output.ptr, output.length);
|
||||||
|
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
return actualLength;
|
return actualLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Provider::loadData(void *output, size_t length, const char *path) {
|
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;
|
return 0;
|
||||||
|
|
||||||
assert(_file->size >= length);
|
assert(file->size >= length);
|
||||||
size_t actualLength = _file->read(output, length);
|
size_t actualLength = file->read(output, length);
|
||||||
|
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
return actualLength;
|
return actualLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Provider::saveData(const void *input, size_t length, const char *path) {
|
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;
|
return 0;
|
||||||
|
|
||||||
size_t actualLength = _file->write(input, length);
|
size_t actualLength = file->write(input, length);
|
||||||
|
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
return actualLength;
|
return actualLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,22 +116,26 @@ size_t Provider::loadTIM(gpu::Image &output, const char *path) {
|
|||||||
data.destroy();
|
data.destroy();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t uploadLength = 0;
|
||||||
|
|
||||||
if (header->flags & (1 << 3)) {
|
if (header->flags & (1 << 3)) {
|
||||||
auto clut = reinterpret_cast<const gpu::TIMSectionHeader *>(section);
|
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;
|
section += clut->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto image = reinterpret_cast<const gpu::TIMSectionHeader *>(section);
|
auto image = reinterpret_cast<const gpu::TIMSectionHeader *>(section);
|
||||||
|
uploadLength += gpu::upload(image->vram, &image[1], true);
|
||||||
gpu::upload(image->vram, &image[1], true);
|
|
||||||
|
|
||||||
data.destroy();
|
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
|
// Sounds should be decompressed and uploaded to the SPU one chunk at a
|
||||||
// time, but whatever.
|
// time, but whatever.
|
||||||
util::Data data;
|
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 header = data.as<const spu::VAGHeader>();
|
||||||
auto body = reinterpret_cast<const uint32_t *>(&header[1]);
|
auto body = reinterpret_cast<const uint32_t *>(&header[1]);
|
||||||
|
|
||||||
if (!output.initFromVAGHeader(header, currentSPUOffset)) {
|
if (!output.initFromVAGHeader(header, offset)) {
|
||||||
data.destroy();
|
data.destroy();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSPUOffset += spu::upload(
|
auto uploadLength =
|
||||||
currentSPUOffset, body, data.length - sizeof(spu::VAGHeader), true
|
spu::upload(offset, body, data.length - sizeof(spu::VAGHeader), true);
|
||||||
);
|
|
||||||
|
|
||||||
data.destroy();
|
data.destroy();
|
||||||
return data.length;
|
return uploadLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct [[gnu::packed]] BMPHeader {
|
struct [[gnu::packed]] BMPHeader {
|
||||||
@ -186,16 +187,16 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
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;
|
return 0;
|
||||||
|
|
||||||
BMPHeader header;
|
BMPHeader header;
|
||||||
|
|
||||||
header.init(rect.w, rect.h, 16);
|
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;
|
util::Data buffer;
|
||||||
|
|
||||||
if (buffer.allocate<uint16_t>(rect.w + 32)) {
|
if (buffer.allocate<uint16_t>(rect.w + 32)) {
|
||||||
@ -224,14 +225,14 @@ size_t Provider::saveVRAMBMP(gpu::RectWH &rect, const char *path) {
|
|||||||
*(ptr++) = newValue;
|
*(ptr++) = newValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
length += _file->write(buffer.ptr, lineLength);
|
length += file->write(buffer.ptr, lineLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.destroy();
|
buffer.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
@ -113,8 +113,6 @@ public:
|
|||||||
|
|
||||||
/* Base file and asset provider classes */
|
/* Base file and asset provider classes */
|
||||||
|
|
||||||
extern uint32_t currentSPUOffset;
|
|
||||||
|
|
||||||
class Provider {
|
class Provider {
|
||||||
public:
|
public:
|
||||||
FileSystemType type;
|
FileSystemType type;
|
||||||
@ -153,7 +151,7 @@ public:
|
|||||||
virtual size_t saveData(const void *input, size_t length, const char *path);
|
virtual size_t saveData(const void *input, size_t length, const char *path);
|
||||||
|
|
||||||
size_t loadTIM(gpu::Image &output, 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);
|
size_t saveVRAMBMP(gpu::RectWH &rect, const char *path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -124,12 +124,12 @@ bool ZIPProvider::init(File *file) {
|
|||||||
_zip.m_pRead = [](
|
_zip.m_pRead = [](
|
||||||
void *opaque, uint64_t offset, void *output, size_t length
|
void *opaque, uint64_t offset, void *output, size_t length
|
||||||
) -> size_t {
|
) -> 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 0;
|
||||||
|
|
||||||
return _file->read(output, length);
|
return file->read(output, length);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!mz_zip_reader_init(&_zip, file->size, _ZIP_FLAGS)) {
|
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
|
// Note that all offsets must be multiples of 2, as writes are done in
|
||||||
// halfwords.
|
// halfwords.
|
||||||
virtual ~Driver(void) {}
|
|
||||||
virtual void write(uint32_t offset, uint16_t value) {}
|
virtual void write(uint32_t offset, uint16_t value) {}
|
||||||
virtual void eraseSector(uint32_t offset) {}
|
virtual void eraseSector(uint32_t offset) {}
|
||||||
virtual void eraseChip(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 {
|
bool IDEDevice::_readDMA(void *data, size_t length) const {
|
||||||
util::assertAligned<uint32_t>(data);
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
|
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT))
|
||||||
|
return false;
|
||||||
|
|
||||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||||
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
||||||
DMA_CHCR(DMA_PIO) = 0
|
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 {
|
bool IDEDevice::_writeDMA(const void *data, size_t length) const {
|
||||||
util::assertAligned<uint32_t>(data);
|
util::assertAligned<uint32_t>(data);
|
||||||
|
|
||||||
|
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT))
|
||||||
|
return false;
|
||||||
|
|
||||||
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
|
||||||
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
DMA_BCR (DMA_PIO) = (length + 3) / 4;
|
||||||
DMA_CHCR(DMA_PIO) = 0
|
DMA_CHCR(DMA_PIO) = 0
|
||||||
|
@ -206,7 +206,6 @@ public:
|
|||||||
inline Device(uint8_t flags = 0)
|
inline Device(uint8_t flags = 0)
|
||||||
: type(NONE), flags(flags), sectorLength(0), capacity(0) {}
|
: type(NONE), flags(flags), sectorLength(0), capacity(0) {}
|
||||||
|
|
||||||
virtual ~Device(void) {}
|
|
||||||
virtual DeviceError enumerate(void) { return UNSUPPORTED_OP; }
|
virtual DeviceError enumerate(void) { return UNSUPPORTED_OP; }
|
||||||
virtual DeviceError poll(void) { return UNSUPPORTED_OP; }
|
virtual DeviceError poll(void) { return UNSUPPORTED_OP; }
|
||||||
virtual void handleInterrupt(void) {}
|
virtual void handleInterrupt(void) {}
|
||||||
|
@ -111,6 +111,46 @@ size_t encodeBase41(char *output, const uint8_t *input, size_t length) {
|
|||||||
|
|
||||||
/* UTF-8 parser */
|
/* 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) {
|
size_t getUTF8StringLength(const char *str) {
|
||||||
for (size_t length = 0;; length++) {
|
for (size_t length = 0;; length++) {
|
||||||
auto value = parseUTF8Character(str);
|
auto value = parseUTF8Character(str);
|
||||||
|
@ -43,17 +43,19 @@ public:
|
|||||||
size_t length;
|
size_t length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
UTF8Character parseUTF8Character(const char *ch);
|
||||||
|
#else
|
||||||
extern "C" uint64_t _parseUTF8Character(const char *ch);
|
extern "C" uint64_t _parseUTF8Character(const char *ch);
|
||||||
size_t getUTF8StringLength(const char *str);
|
|
||||||
|
|
||||||
static inline UTF8Character parseUTF8Character(const char *ch) {
|
static inline UTF8Character parseUTF8Character(const char *ch) {
|
||||||
auto values = _parseUTF8Character(ch);
|
auto values = _parseUTF8Character(ch);
|
||||||
|
|
||||||
return {
|
return { UTF8CodePoint(values), size_t(values >> 32) };
|
||||||
.codePoint = UTF8CodePoint(values),
|
|
||||||
.length = size_t(values >> 32)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t getUTF8StringLength(const char *str);
|
||||||
|
|
||||||
/* LZ4 decompressor */
|
/* LZ4 decompressor */
|
||||||
|
|
||||||
|
@ -50,14 +50,16 @@ template<typename T, typename E> T Tween<T, E>::getValue(int time) const {
|
|||||||
) / TWEEN_UNIT;
|
) / 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, LinearEasing>;
|
||||||
template class Tween<int, QuadInEasing>;
|
template class Tween<int, QuadInEasing>;
|
||||||
template class Tween<int, QuadOutEasing>;
|
template class Tween<int, QuadOutEasing>;
|
||||||
template class Tween<uint16_t, LinearEasing>;
|
template class Tween<int8_t, LinearEasing>;
|
||||||
template class Tween<uint16_t, QuadInEasing>;
|
template class Tween<int8_t, QuadInEasing>;
|
||||||
template class Tween<uint16_t, QuadOutEasing>;
|
template class Tween<int8_t, QuadOutEasing>;
|
||||||
template class Tween<uint32_t, LinearEasing>;
|
template class Tween<int16_t, LinearEasing>;
|
||||||
template class Tween<uint32_t, QuadInEasing>;
|
template class Tween<int16_t, QuadInEasing>;
|
||||||
template class Tween<uint32_t, QuadOutEasing>;
|
template class Tween<int16_t, QuadOutEasing>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -272,10 +272,10 @@ void App::_loadResources(void) {
|
|||||||
res.loadTIM(_splashOverlay.image, "assets/textures/splash.tim");
|
res.loadTIM(_splashOverlay.image, "assets/textures/splash.tim");
|
||||||
res.loadData(_stringTable, "assets/lang/en.lang");
|
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++)
|
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) {
|
bool App::_createDataDirectory(void) {
|
||||||
|
@ -267,15 +267,15 @@ bool App::_cartRestoreWorker(void) {
|
|||||||
_workerStatus.update(0, 3, WSTR("App.cartRestoreWorker.init"));
|
_workerStatus.update(0, 3, WSTR("App.cartRestoreWorker.init"));
|
||||||
|
|
||||||
const char *path = _fileBrowserScreen.selectedPath;
|
const char *path = _fileBrowserScreen.selectedPath;
|
||||||
auto _file = _fileIO.vfs.openFile(path, fs::READ);
|
auto file = _fileIO.vfs.openFile(path, fs::READ);
|
||||||
|
|
||||||
cart::CartDump newDump;
|
cart::CartDump newDump;
|
||||||
|
|
||||||
if (_file) {
|
if (file) {
|
||||||
auto length = _file->read(&newDump, sizeof(newDump));
|
auto length = file->read(&newDump, sizeof(newDump));
|
||||||
|
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
|
|
||||||
if (length < (sizeof(newDump) - sizeof(newDump.data)))
|
if (length < (sizeof(newDump) - sizeof(newDump.data)))
|
||||||
goto _fileError;
|
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;
|
static constexpr int _LOOP_FADE_IN_TIME = 30;
|
||||||
|
|
||||||
void AboutScreen::show(ui::Context &ctx, bool goBack) {
|
void AboutScreen::show(ui::Context &ctx, bool goBack) {
|
||||||
|
@ -65,7 +65,7 @@ private:
|
|||||||
util::Data _text;
|
util::Data _text;
|
||||||
spu::Channel _loopChannel;
|
spu::Channel _loopChannel;
|
||||||
|
|
||||||
util::Tween<uint16_t, util::QuadInEasing> _loopVolume;
|
util::Tween<int, util::QuadInEasing> _loopVolume;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void show(ui::Context &ctx, bool goBack = false);
|
void show(ui::Context &ctx, bool goBack = false);
|
||||||
|
@ -151,12 +151,12 @@ bool App::_executableWorker(void) {
|
|||||||
} else {
|
} else {
|
||||||
__builtin_memset(header.magic, 0, sizeof(header.magic));
|
__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) {
|
if (file) {
|
||||||
_file->read(&header, sizeof(header));
|
file->read(&header, sizeof(header));
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!header.validateMagic()) {
|
if (!header.validateMagic()) {
|
||||||
|
@ -221,7 +221,7 @@ void FilePickerScreen::show(ui::Context &ctx, bool goBack) {
|
|||||||
_listLength = 0;
|
_listLength = 0;
|
||||||
|
|
||||||
#ifdef ENABLE_PCDRV
|
#ifdef ENABLE_PCDRV
|
||||||
_addDevice(nullptr, APP->_fileIO.host, "host:");
|
_addDevice(nullptr, &(APP->_fileIO.host), "host:");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (size_t i = 0; i < util::countOf(APP->_fileIO.ideDevices); i++)
|
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);
|
snprintf(filePath, sizeof(filePath), entry.path, dirPath);
|
||||||
|
|
||||||
auto _file = _fileIO.vfs.openFile(
|
auto file = _fileIO.vfs.openFile(
|
||||||
filePath, fs::WRITE | fs::ALLOW_CREATE
|
filePath, fs::WRITE | fs::ALLOW_CREATE
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_file)
|
if (!file)
|
||||||
goto _fileError;
|
goto _fileError;
|
||||||
|
|
||||||
util::Data buffer;
|
util::Data buffer;
|
||||||
@ -161,10 +161,10 @@ bool App::_romDumpWorker(void) {
|
|||||||
_workerStatus.update(i, numChunks, WSTRH(entry.dumpPrompt));
|
_workerStatus.update(i, numChunks, WSTRH(entry.dumpPrompt));
|
||||||
entry.region.read(buffer.ptr, offset, chunkLength);
|
entry.region.read(buffer.ptr, offset, chunkLength);
|
||||||
|
|
||||||
if (_file->write(buffer.ptr, chunkLength) < chunkLength) {
|
if (file->write(buffer.ptr, chunkLength) < chunkLength) {
|
||||||
buffer.destroy();
|
buffer.destroy();
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
|
|
||||||
goto _fileError;
|
goto _fileError;
|
||||||
}
|
}
|
||||||
@ -173,8 +173,8 @@ bool App::_romDumpWorker(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer.destroy();
|
buffer.destroy();
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
|
|
||||||
LOG_APP("%s saved", filePath);
|
LOG_APP("%s saved", filePath);
|
||||||
}
|
}
|
||||||
@ -201,9 +201,9 @@ bool App::_romRestoreWorker(void) {
|
|||||||
_workerStatus.update(0, 1, WSTR("App.romRestoreWorker.init"));
|
_workerStatus.update(0, 1, WSTR("App.romRestoreWorker.init"));
|
||||||
|
|
||||||
const char *path = _fileBrowserScreen.selectedPath;
|
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(
|
_messageScreen.setMessage(
|
||||||
MESSAGE_ERROR, WSTR("App.romRestoreWorker.fileError"), path
|
MESSAGE_ERROR, WSTR("App.romRestoreWorker.fileError"), path
|
||||||
);
|
);
|
||||||
@ -243,8 +243,8 @@ bool App::_romRestoreWorker(void) {
|
|||||||
size_t j = numChips; j > 0; j--, bufferPtr += maxChunkLength,
|
size_t j = numChips; j > 0; j--, bufferPtr += maxChunkLength,
|
||||||
offset += chipLength
|
offset += chipLength
|
||||||
) {
|
) {
|
||||||
_file->seek(offset);
|
file->seek(offset);
|
||||||
auto length = _file->read(bufferPtr, maxChunkLength);
|
auto length = file->read(bufferPtr, maxChunkLength);
|
||||||
|
|
||||||
// Data is written 16 bits at a time, so the chunk must be padded to
|
// Data is written 16 bits at a time, so the chunk must be padded to
|
||||||
// an even number of bytes.
|
// an even number of bytes.
|
||||||
@ -301,8 +301,8 @@ bool App::_romRestoreWorker(void) {
|
|||||||
|
|
||||||
buffers.destroy();
|
buffers.destroy();
|
||||||
chunkLengths.destroy();
|
chunkLengths.destroy();
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
delete driver;
|
delete driver;
|
||||||
|
|
||||||
_messageScreen.setMessage(
|
_messageScreen.setMessage(
|
||||||
@ -316,14 +316,14 @@ bool App::_romRestoreWorker(void) {
|
|||||||
|
|
||||||
util::Hash message;
|
util::Hash message;
|
||||||
|
|
||||||
message = (_file->size > regionLength)
|
message = (file->size > regionLength)
|
||||||
? "App.romRestoreWorker.overflow"_h
|
? "App.romRestoreWorker.overflow"_h
|
||||||
: "App.romRestoreWorker.success"_h;
|
: "App.romRestoreWorker.success"_h;
|
||||||
|
|
||||||
buffers.destroy();
|
buffers.destroy();
|
||||||
chunkLengths.destroy();
|
chunkLengths.destroy();
|
||||||
_file->close();
|
file->close();
|
||||||
delete _file;
|
delete file;
|
||||||
delete driver;
|
delete driver;
|
||||||
|
|
||||||
_messageScreen.setMessage(MESSAGE_SUCCESS, WSTRH(message), bytesWritten);
|
_messageScreen.setMessage(MESSAGE_SUCCESS, WSTRH(message), bytesWritten);
|
||||||
|
@ -133,7 +133,6 @@ public:
|
|||||||
inline CartParser(CartDump &dump, uint8_t flags = 0)
|
inline CartParser(CartDump &dump, uint8_t flags = 0)
|
||||||
: _dump(dump), flags(flags) {}
|
: _dump(dump), flags(flags) {}
|
||||||
|
|
||||||
virtual ~CartParser(void) {}
|
|
||||||
virtual size_t getCode(char *output) const { return 0; }
|
virtual size_t getCode(char *output) const { return 0; }
|
||||||
virtual void setCode(const char *input) {}
|
virtual void setCode(const char *input) {}
|
||||||
virtual size_t getRegion(char *output) const { return 0; }
|
virtual size_t getRegion(char *output) const { return 0; }
|
||||||
@ -219,7 +218,6 @@ public:
|
|||||||
inline ROMHeaderParser(ROMHeaderDump &dump, uint8_t flags = 0)
|
inline ROMHeaderParser(ROMHeaderDump &dump, uint8_t flags = 0)
|
||||||
: _dump(dump), flags(flags) {}
|
: _dump(dump), flags(flags) {}
|
||||||
|
|
||||||
virtual ~ROMHeaderParser(void) {}
|
|
||||||
virtual size_t getCode(char *output) const { return 0; }
|
virtual size_t getCode(char *output) const { return 0; }
|
||||||
virtual void setCode(const char *input) {}
|
virtual void setCode(const char *input) {}
|
||||||
virtual size_t getRegion(char *output) const { return 0; }
|
virtual size_t getRegion(char *output) const { return 0; }
|
||||||
|
@ -47,7 +47,6 @@ public:
|
|||||||
inline Driver(CartDump &dump)
|
inline Driver(CartDump &dump)
|
||||||
: _dump(dump) {}
|
: _dump(dump) {}
|
||||||
|
|
||||||
virtual ~Driver(void) {}
|
|
||||||
virtual DriverError readSystemID(void) { return UNSUPPORTED_OP; }
|
virtual DriverError readSystemID(void) { return UNSUPPORTED_OP; }
|
||||||
virtual DriverError readCartID(void) { return UNSUPPORTED_OP; }
|
virtual DriverError readCartID(void) { return UNSUPPORTED_OP; }
|
||||||
virtual DriverError readPublicData(void) { return UNSUPPORTED_OP; }
|
virtual DriverError readPublicData(void) { return UNSUPPORTED_OP; }
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "common/gpu.hpp"
|
#include "common/gpu.hpp"
|
||||||
#include "common/io.hpp"
|
#include "common/io.hpp"
|
||||||
#include "common/ioboard.hpp"
|
#include "common/ioboard.hpp"
|
||||||
|
#include "common/mdec.hpp"
|
||||||
#include "common/spu.hpp"
|
#include "common/spu.hpp"
|
||||||
#include "main/app/app.hpp"
|
#include "main/app/app.hpp"
|
||||||
#include "main/uibase.hpp"
|
#include "main/uibase.hpp"
|
||||||
@ -35,6 +36,7 @@ int main(int argc, const char **argv) {
|
|||||||
installExceptionHandler();
|
installExceptionHandler();
|
||||||
gpu::init();
|
gpu::init();
|
||||||
spu::init();
|
spu::init();
|
||||||
|
mdec::init();
|
||||||
io::init();
|
io::init();
|
||||||
util::initZipCRC32();
|
util::initZipCRC32();
|
||||||
|
|
||||||
|
@ -369,6 +369,26 @@ typedef enum {
|
|||||||
/* MDEC */
|
/* MDEC */
|
||||||
|
|
||||||
typedef enum {
|
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_BITMASK = 7 << 16,
|
||||||
MDEC_STAT_BLOCK_Y0 = 0 << 16,
|
MDEC_STAT_BLOCK_Y0 = 0 << 16,
|
||||||
MDEC_STAT_BLOCK_Y1 = 1 << 16,
|
MDEC_STAT_BLOCK_Y1 = 1 << 16,
|
||||||
@ -376,6 +396,13 @@ typedef enum {
|
|||||||
MDEC_STAT_BLOCK_Y3 = 3 << 16,
|
MDEC_STAT_BLOCK_Y3 = 3 << 16,
|
||||||
MDEC_STAT_BLOCK_CR = 4 << 16,
|
MDEC_STAT_BLOCK_CR = 4 << 16,
|
||||||
MDEC_STAT_BLOCK_CB = 5 << 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_OUT = 1 << 27,
|
||||||
MDEC_STAT_DREQ_IN = 1 << 28,
|
MDEC_STAT_DREQ_IN = 1 << 28,
|
||||||
MDEC_STAT_BUSY = 1 << 29,
|
MDEC_STAT_BUSY = 1 << 29,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user