feat(cd): load Jaguar CD CHDs that carry CHSE session tags (#322)

Refuse pre-15886 rips, vendor libchdr, and ship pinned chdman helpers
on tagged releases and nightlies so users can convert CUE/BIN themselves.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Joseph MattielloandCursor authored and Joe Mattiello committed 2026-08-16 01:29:50 -04:00
1 parent 9b4c604eab
commit 6453b22baa
84 files changed
+61975 -101

No files matched your search

+90
View File
@@ -0,0 +1,90 @@
name: CHD tools
# Desktop-only chdman + jagcd helpers. Consoles never write CHDs, and MAME
# is too heavy to fold into the 16-platform core matrix. release.yml calls
# this workflow so tagged releases and nightlies attach the same zips as
# the cores. workflow_dispatch rebuilds the artifacts without publishing.
on:
workflow_dispatch:
workflow_call:
permissions:
contents: read
jobs:
build-tools:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
artifact: jagcd-tools-linux-x64
- os: macos-latest
artifact: jagcd-tools-macos
- os: windows-latest
artifact: jagcd-tools-windows-x64
runs-on: ${{ matrix.os }}
defaults:
run:
shell: ${{ matrix.os == 'windows-latest' && 'msys2 {0}' || 'bash' }}
steps:
- uses: actions/checkout@v7
- uses: msys2/setup-msys2@v2
if: matrix.os == 'windows-latest'
with:
msystem: MINGW64
update: true
install: >-
git make python zip
mingw-w64-x86_64-gcc mingw-w64-x86_64-python
- name: Read chdman pin
id: pin
run: |
PIN=$(grep -E '^[0-9a-f]{7,}' tools/jagcd/CHDMAN_PIN | tail -n 1)
echo "sha=$PIN" >> "$GITHUB_OUTPUT"
echo "pin=$PIN"
- name: Clone MAME at pin
run: |
git clone --filter=blob:none --no-checkout https://github.com/mamedev/mame.git mame
cd mame
git fetch --depth 1 origin ${{ steps.pin.outputs.sha }}
git checkout FETCH_HEAD
- name: Build chdman
working-directory: mame
run: |
make -j$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4) \
TOOLS=1 EMULATOR=0 NOWERROR=1 PRECOMPILE=0 REGENIE=1
- name: Build jagcd-chd-check
run: |
gcc -O2 -std=c99 \
-I deps/libchdr/include \
-I deps/libchdr/deps/lzma-25.01/include \
-I deps/libchdr/deps/miniz-3.1.1 \
-I deps/libchdr/deps/zstd-1.5.7 \
-D_7ZIP_ST \
-o tools/jagcd/jagcd-chd-check \
tools/jagcd/jagcd-chd-check.c deps/libchdr/unity.c
- name: Package
run: |
mkdir -p dist-jagcd
CHDMAN=$(find mame \( -name chdman -o -name chdman.exe \) | head -1)
test -n "$CHDMAN"
cp "$CHDMAN" dist-jagcd/
cp tools/jagcd/jagcd-to-chd tools/jagcd/jagcd-to-chd.cmd \
tools/jagcd/jagcd-chd-check tools/jagcd/CHDMAN_PIN \
docs/jagcd-chd.md dist-jagcd/
chmod +x dist-jagcd/jagcd-to-chd dist-jagcd/jagcd-chd-check \
dist-jagcd/chdman dist-jagcd/chdman.exe 2>/dev/null || true
cd dist-jagcd && zip -r ../${{ matrix.artifact }}.zip .
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact }}
path: ${{ matrix.artifact }}.zip
if-no-files-found: error
+13 -2
View File
@@ -435,6 +435,17 @@ jobs:
path: dist/
if-no-files-found: error
# ----------------------------------------------------------------
# Desktop jagcd tools (pinned chdman + jagcd-to-chd + jagcd-chd-check).
# Same three hosts as cue2cdi. Runs in parallel with the core matrix;
# the release/nightly jobs wait on it so the zips land in SHA256SUMS
# and on the GitHub release next to the cores.
# ----------------------------------------------------------------
jagcd-tools:
name: jagcd tools (chdman)
needs: [guard]
uses: ./.github/workflows/chdman-tools.yml
# ----------------------------------------------------------------
# Release: collect all artifacts, generate SHA256SUMS, build the
# release body from docs/RELEASE_NOTES_v<TAG>.md (artifact list
@@ -442,7 +453,7 @@ jobs:
# tagged release. See SECURITY.md for binary verification docs.
# ----------------------------------------------------------------
release:
needs: [build, vita-build, switch-build]
needs: [build, vita-build, switch-build, jagcd-tools]
# Tag pushes only. Everything below assumes GITHUB_REF_NAME is a vX.Y.Z
# tag (release-notes lookup, release title, `gh release create <TAG>`), so
# this guard is what keeps a develop push from creating a release literally
@@ -557,7 +568,7 @@ jobs:
# ----------------------------------------------------------------
nightly:
name: Update nightly prerelease
needs: [build, vita-build, switch-build]
needs: [build, vita-build, switch-build, jagcd-tools]
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
permissions:
+3
View File
@@ -50,6 +50,9 @@ test/tools/test_*
!test/tools/test_*.sh
!test/tools/test_*.py
# Hand-built tool binaries that don't match the test_* prefix.
tools/jagcd/jagcd-chd-check
tools/jagcd/chdman
tools/jagcd/*.exe
test/tools/blit_memo_verify
test/tools/i2s_lag_probe
test/tools/test_memory_map
+1 -1
View File
@@ -54,7 +54,7 @@ Frame loop is event-driven (not cycle-accurate): `JaguarExecuteNew()` in `src/co
- `src/core/` — orchestration, memory map, events, settings, files, cheats
- `src/tom/` — video, GPU, OP, blitter (+ SIMD)
- `src/jerry/` — audio, DSP, DAC, EEPROM, input, wavetable, UART/netlink (`uart.c` + `jlink.c`, see `docs/netlink-design.md`)
- `src/cd/` — Jaguar CD: BUTCH/FIFO/DSA/Q-subcode in `cdrom.c`, image loading (CUE/BIN, CDI — **no CHD**, removed during the CD overhaul; see issue #322) in `cdintf.c`; BIOS auth bypass + boot stub in `src/core/jaguar.c`
- `src/cd/` — Jaguar CD: BUTCH/FIFO/DSA/Q-subcode in `cdrom.c`, image loading (CUE/BIN, CDI, CHD) in `cdintf.c`; BIOS auth bypass + boot stub in `src/core/jaguar.c`. CHD requires `CHSE` session tags from a post-2026-08 chdman — old internet CHDs are refused; see [`docs/jagcd-chd.md`](docs/jagcd-chd.md) and issue #322.
- `src/core/jaggd.c` — Jaguar GameDrive: SPI mailbox at `$F16000`, embedded GDBIOS blob, 6×1MB page → 16-bank switching for images up to 16 MB (spec: `docs/jgd-interface-notes.md`)
- `src/core/titledb.c` — per-title enhancement defaults (#368); applied at option-read time in `libretro.c`, user-set values always win
- `src/core/titlehook.c` — per-title enhancement **hooks** (#370): verified byte patches into cartridge ROM at load, gated by `virtualjaguar_enhancement_hooks` (default **disabled**). Ships with **zero** rows — the mechanism, not the data. Authoring rules + the three non-obvious fences (GameDrive banked image, cart entry vector `$400..$407`, `TitleHook*` needing its own export-list entry) in [`docs/enhancement-hooks.md`](docs/enhancement-hooks.md). Not a general scripting surface: if a behaviour fits a `{key, value}` string it is a `pairs[]` entry, and an emulator timing bug is never a hook
+31 -5
View File
@@ -603,7 +603,7 @@ CORE_DIR := .
include Makefile.common
OBJECTS := $(SOURCES_CXX:.cpp=.o) $(SOURCES_C:.c=.o)
OBJECTS := $(SOURCES_CXX:.cpp=.o) $(SOURCES_C:.c=.o) $(SOURCES_LIBCHDR:.c=.o)
# ----------------------------------------------------------------
# version.h: generated header read by libretro.c. Single source of
@@ -890,6 +890,12 @@ else
$(LD) $(LINKOUT)$@ $(OBJECTS) $(LDFLAGS)
endif
# libchdr is C99; the rest of the core is gnu89. A dedicated rule so
# the generic %.o: %.c line cannot compile unity.c as C89. Must sit
# AFTER `all:` so it is not the default goal.
$(LIBCHDR_DIR)/unity.o: $(LIBCHDR_DIR)/unity.c
$(CC) -c $(OBJOUT)$@ $< $(CFLAGS) $(LIBCHDR_CFLAGS) -Wno-unused-function -Wno-unused-variable
# version.h dependency hook (must come after `all:` so Make 3.81 on
# stock macOS doesn't latch onto libretro.o as the default goal).
$(CORE_DIR)/libretro.o: $(VERSION_H)
@@ -907,10 +913,11 @@ clean:
test/test_tom_visible_window test/test_framebuffer_integrity \
test/test_butch_cd test/test_bios_config test/test_boot_config \
test/test_cart_format \
test/test_cd_boot test/test_cd_hle_boot test/test_cd_bios_boot test/test_cd_toc_contract test/test_cd_fifo_stream test/test_cd_ssi_stream test/test_cd_second_transfer test/test_cd_hle_idempotent test/test_cd_lost_wakeup test/test_cd_pregap test/test_cd_synth_read test/test_cd_synth_butch test/test_cd_synth_cdda test/test_cd_synth_subq \
test/test_cd_boot test/test_cd_hle_boot test/test_cd_bios_boot test/test_cd_toc_contract test/test_cd_fifo_stream test/test_cd_ssi_stream test/test_cd_second_transfer test/test_cd_hle_idempotent test/test_cd_lost_wakeup test/test_cd_pregap test/test_cd_chd test/test_cd_synth_read test/test_cd_synth_butch test/test_cd_synth_cdda test/test_cd_synth_subq \
test/test_audio_dac test/test_blitter \
test/test_state_compat test/test_frontend_pacing test/test_jgd \
test/dump_pc test/heap_search \
tools/jagcd/jagcd-chd-check \
test/tools/test_memory_map test/tools/test_option_visibility test/test_memtrack test/test_nvmbios test/tools/test_dsp_audio_diag \
test/tools/test_frame_timing test/tools/test_runahead_determinism test/tools/test_pertitle_db \
test/test_titledb test/test_titlehook test/tools/test_hook_gate \
@@ -967,14 +974,15 @@ test: test/test_dram_timing test/test_cheat test/test_event_queue test/test_jlin
test/tools/test_runahead_determinism test/tools/test_wedge_spin \
test/test_butch_cd test/test_bios_config test/test_boot_config \
test/test_cart_format \
test/test_cd_boot test/test_cd_hle_boot test/test_cd_bios_boot test/test_cd_toc_contract test/test_cd_fifo_stream test/test_cd_ssi_stream test/test_cd_second_transfer test/test_cd_hle_idempotent test/test_cd_lost_wakeup test/test_cd_pregap test/test_cd_synth_read test/test_cd_synth_butch test/test_cd_synth_cdda test/test_cd_synth_subq \
test/test_cd_boot test/test_cd_hle_boot test/test_cd_bios_boot test/test_cd_toc_contract test/test_cd_fifo_stream test/test_cd_ssi_stream test/test_cd_second_transfer test/test_cd_hle_idempotent test/test_cd_lost_wakeup test/test_cd_pregap test/test_cd_chd test/test_cd_synth_read test/test_cd_synth_butch test/test_cd_synth_cdda test/test_cd_synth_subq \
test/test_audio_dac test/test_blitter \
test/tools/test_memory_map test/tools/test_op_gpu_object test/tools/test_option_visibility test/test_memtrack test/test_nvmbios test/test_uart_core test/test_netlink_host \
test/tools/netlink_pair test/tools/netlink_latency test/tools/netlink_delay_proxy test/tools/test_pertitle_db \
test/tools/test_hook_gate \
test/tools/i2s_lag_probe test/tools/joymatrix_identity \
test/test_quadrature test/tools/mouse_decode_test \
test/tools/rotary_decode_test
test/tools/rotary_decode_test \
tools/jagcd/jagcd-chd-check
@# Skip ledger: truncate FIRST so a previous run's rows cannot resurface
@# as fresh skips (the stale-row failure mode documented for
@# cd_boot_matrix.sh). Every optional check below records into it, and
@@ -1190,6 +1198,16 @@ test: test/test_dram_timing test/test_cheat test/test_event_queue test/test_jlin
./test/test_butch_cd
./test/test_cd_hle_idempotent
./test/test_cd_pregap
./test/test_cd_chd
@# Optional: PATH/JAGCD_CHDMAN round-trip. Exit 77 = no CHSE-capable
@# chdman (CI, Homebrew 0.288). The committed synth_jagcd*.chd files
@# are the actual CHD load gate; this only checks the converter.
@if bash test/tools/jagcd_roundtrip.sh; then :; \
else rc=$$?; \
if [ $$rc -eq 77 ]; then \
bash scripts/test-skip.sh record "jagcd CUE->CHD round-trip" "no CHSE-capable chdman on PATH"; \
else exit $$rc; fi; \
fi
./test/test_cd_synth_read
./test/test_cd_synth_butch
./test/test_cd_synth_cdda
@@ -1728,6 +1746,14 @@ test/test_cd_pregap: test/test_cd_pregap.c test/test_framework.h test/cd_asserti
$(CC) -O2 -Wall -Wno-unused-function -Wno-unused-variable -std=c99 $(INCFLAGS) \
-o $@ test/test_cd_pregap.c -ldl
test/test_cd_chd: test/test_cd_chd.c test/test_framework.h test/cd_assertions.h
$(CC) -O2 -Wall -Wno-unused-function -Wno-unused-variable -std=c99 $(INCFLAGS) \
-o $@ test/test_cd_chd.c -ldl
tools/jagcd/jagcd-chd-check: tools/jagcd/jagcd-chd-check.c $(SOURCES_LIBCHDR)
$(CC) -O2 -Wall -std=c99 $(INCFLAGS) $(LIBCHDR_CFLAGS) \
-o $@ tools/jagcd/jagcd-chd-check.c $(SOURCES_LIBCHDR)
test/test_cd_synth_read: test/test_cd_synth_read.c test/test_framework.h test/cd_assertions.h
$(CC) -O2 -Wall -Wno-unused-function -Wno-unused-variable -std=c99 $(INCFLAGS) \
-o $@ test/test_cd_synth_read.c -ldl
@@ -1791,7 +1817,7 @@ test/heap_search: test/heap_search.c
# Aggregate target for the manual diagnostic tools.
.PHONY: tools
tools: test/dump_pc test/heap_search test/test_cd_boot
tools: test/dump_pc test/heap_search test/test_cd_boot tools/jagcd/jagcd-chd-check
endif
.PHONY: clean test lint coverage benchmark acid dsp-diag frame-timing cue2cdi \
+14 -1
View File
@@ -1,5 +1,7 @@
LIBRETRO_COMM_DIR = $(CORE_DIR)/libretro-common
LIBCHDR_DIR := $(CORE_DIR)/deps/libchdr
INCFLAGS := -I$(CORE_DIR) \
-I$(CORE_DIR)/src \
-I$(CORE_DIR)/src/core \
@@ -8,7 +10,18 @@ INCFLAGS := -I$(CORE_DIR) \
-I$(CORE_DIR)/src/cd \
-I$(CORE_DIR)/src/bios \
-I$(CORE_DIR)/src/m68000 \
-I$(LIBRETRO_COMM_DIR)/include
-I$(LIBRETRO_COMM_DIR)/include \
-I$(LIBCHDR_DIR)/include \
-I$(LIBCHDR_DIR)/deps/lzma-25.01/include \
-I$(LIBCHDR_DIR)/deps/miniz-3.1.1 \
-I$(LIBCHDR_DIR)/deps/zstd-1.5.7
# libchdr is C99 (vendored). Compiled as its own translation unit
# (unity.c) with -std=c99; see the unity.o rule in the Makefile.
# _7ZIP_ST is the LZMA single-thread decode config libchdr expects.
LIBCHDR_CFLAGS := -std=c99 -D_7ZIP_ST \
-DWANT_RAW_DATA_SECTOR=1 -DWANT_SUBCODE=1 -DVERIFY_BLOCK_CRC=1
SOURCES_LIBCHDR := $(LIBCHDR_DIR)/unity.c
ifneq (,$(findstring msvc2003,$(platform)))
INCFLAGS += -I$(LIBRETRO_COMM_DIR)/include/compat/msvc
+1 -1
View File
@@ -140,7 +140,7 @@ libretro also hosts a community-maintained **[Atari Jaguar compatibility list](h
1. **Install the core.** In RetroArch: *Main Menu → Online Updater → Core Downloader → Atari - Jaguar (Virtual Jaguar)*. Or download a build from [Releases](https://github.com/libretro/virtualjaguar-libretro/releases) and drop it in your `cores` folder.
2. **No BIOS files needed.** Nothing to hunt down — see [BIOS](#bios).
3. **Load a game.** Supported: `.j64`, `.jag`, `.rom`, `.abs`, `.cof`, `.bin`, `.prg` (including inside ZIP archives), plus `.cue` and `.cdi` for Jaguar CD images, and conservative headerless raw homebrew loading.
3. **Load a game.** Supported: `.j64`, `.jag`, `.rom`, `.abs`, `.cof`, `.bin`, `.prg` (including inside ZIP archives), plus `.cue`, `.cdi`, and `.chd` for Jaguar CD images, and conservative headerless raw homebrew loading. CHD files must include session metadata from a current chdman; see [`docs/jagcd-chd.md`](docs/jagcd-chd.md).
### Core options worth knowing
+88
View File
@@ -0,0 +1,88 @@
# Changelog
All notable changes to libchdr are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.0] - Unreleased
First formally tagged release. Project has been depended on by
RetroArch, DuckStation, Flycast, SwanStation and others for years;
this tag exists so Linux distros can ship a stable reference point
instead of a dated snapshot.
### Added
- CHDv5 read support (backported from MAME C++).
- Zstandard (zstd) compression codec.
- `CHDR_WANT_RAW_DATA_SECTOR`, `CHDR_WANT_SUBCODE`,
`CHDR_VERIFY_BLOCK_CRC` CMake options for reduced-scope builds.
- `WITH_SYSTEM_ZLIB` and `WITH_SYSTEM_ZSTD` CMake options for distros.
- `BUILD_FUZZER` CMake option (libFuzzer + ASan) and `tests/fuzz.c`.
- `BUILD_LTO` CMake option.
- `chd_read_header_core_file_callbacks` public API.
- pkg-config file (`libchdr.pc`) installed alongside the library.
- Unity-build script (`unity.c`).
### Changed
- Library is now split across per-codec translation units
(`src/libchdr_codec_*.c`) instead of one monolithic `libchdr_chd.c`.
- Internal codec headers moved from `include/libchdr/` to `src/` so
they are no longer part of the installed header set (#144).
- `cd_codec_decompress` signature is now stable across builds; it no
longer changes with the `WANT_SUBCODE` preprocessor flag (#144).
- `lzma_allocator` now embeds `ISzAlloc` as its first member, matching
the LZMA SDK's expected layout (#144).
- Bundled `zlib` replaced with `miniz` 3.1.1 (single-file).
- Bundled `zstd` replaced with a single-file version of itself
(1.5.7).
- Bundled LZMA updated to 25.01.
- Bundled `dr_flac` updated to 0.13.3 (includes fix for
CVE-2025-14369 integer overflow).
- pkg-config `Version:` now emits the full `MAJOR.MINOR.PATCH`.
- Exported symbols are restricted to `chd_*` via a linker version
script on ELF targets, `-exported_symbol _chd_*` on macOS.
### Fixed
- `chd_read_header_core_file_callbacks` now works for CHD versions
below 4 (#146).
- v1/v2 header: `hunkbytes` is computed in `uint64_t` and rejected if
the product of `seclen * obsolete_hunksize` overflows `uint32_t` or
is zero (#148).
- `metadata_find_entry` caps traversal at 65536 entries, preventing
unbounded seek+read loops on malformed CHDs with cyclic
`next`-pointer chains (#148).
- `fseeko` / `ftello` are now declared under strict C11 builds
(`-Werror=implicit-function-declaration`) and on Debian armhf
`time64` rebuilds: feature test macros are defined before any
system header include, and the `fseeko64`/`ftello64` branch has
been removed in favor of LFS-aliased `fseeko`/`ftello` (#92, #117).
- `chd_get_metadata` fallback now uses `snprintf` instead of
`sprintf`.
- `fseek` errors are now propagated as `CHDERR_READ_ERROR`.
- Only reads as much header data as the declared header length.
- Various memory and overflow bugs in hunk decoding (#132).
### Removed
- Bundled `zlib-1.3.1/` directory (dead since the switch to miniz).
### Known limitations
- **AVHuff codec is not implemented** (#69). CHDs produced with the
`CHDCOMPRESSION_AV` codec (CHDv1-v4) or the `avhu` codec tag (CHDv5)
return `CHDERR_UNSUPPORTED_FORMAT`. In practice this only affects
laserdisc CHDs (Dragon's Lair, Space Ace, Time Traveler, and other
MAME/Daphne/Hypseus Singe targets). All other consumers (PS1, PS2,
Saturn, Dreamcast, arcade HD, etc.) are unaffected.
## ABI
`SOVERSION = PROJECT_VERSION_MAJOR`. Bumped only on ABI breaks. The
`v0.x` series ships as `libchdr.so.0`; a future `v1.0.0` would ship
as `libchdr.so.1`. The public header set is restricted to
`include/libchdr/` and the public symbol set to `chd_*`.
[0.3.0]: https://github.com/rtissera/libchdr/releases/tag/v0.3.0
+170
View File
@@ -0,0 +1,170 @@
cmake_minimum_required(VERSION 3.10)
project(chdr VERSION 0.3.0 LANGUAGES C)
if(CMAKE_PROJECT_NAME STREQUAL "chdr")
option(BUILD_SHARED_LIBS "Build libchdr also as a shared library" ON)
endif()
option(INSTALL_STATIC_LIBS "Install static libraries" OFF)
option(WITH_SYSTEM_ZLIB "Use system provided zlib library" OFF)
option(WITH_SYSTEM_ZSTD "Use system provided zstd library" OFF)
option(CHDR_WANT_RAW_DATA_SECTOR "Output ECC data and sync header" ON)
option(CHDR_WANT_SUBCODE "Output CD subchannel data" ON)
option(CHDR_VERIFY_BLOCK_CRC "Verify integrity of decoded data" ON)
option(BUILD_LTO "Compile libchdr with link-time optimization if supported" OFF)
if(BUILD_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT HAVE_IPO)
if(HAVE_IPO)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
option(BUILD_FUZZER "Build instrumented binary for fuzzing with libfuzzer, requires clang")
if(BUILD_FUZZER)
# Override CFLAGS early for instrumentation. Disable shared libs for instrumentation.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,fuzzer-no-link")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,fuzzer-no-link")
set(BUILD_SHARED_LIBS OFF)
endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(GNUInstallDirs)
#--------------------------------------------------
# dependencies
#--------------------------------------------------
# lzma
if(NOT TARGET chdr-lzma)
add_subdirectory(deps/lzma-25.01 EXCLUDE_FROM_ALL)
endif()
list(APPEND CHDR_LIBS chdr-lzma)
# zlib
if (WITH_SYSTEM_ZLIB)
find_package(ZLIB REQUIRED)
list(APPEND PLATFORM_LIBS ZLIB::ZLIB)
list(APPEND CHDR_DEFINES CHDR_SYSTEM_ZLIB)
else()
if(NOT TARGET miniz)
add_subdirectory(deps/miniz-3.1.1 EXCLUDE_FROM_ALL)
endif()
list(APPEND CHDR_LIBS miniz)
endif()
# zstd
if (WITH_SYSTEM_ZSTD)
find_package(zstd MODULE REQUIRED)
list(APPEND PLATFORM_LIBS zstd::zstd)
list(APPEND CHDR_DEFINES CHDR_SYSTEM_ZSTD)
else()
if(NOT TARGET zstd)
add_subdirectory(deps/zstd-1.5.7 EXCLUDE_FROM_ALL)
endif()
list(APPEND CHDR_LIBS zstd)
endif()
#--------------------------------------------------
# options
#--------------------------------------------------
if(CHDR_WANT_RAW_DATA_SECTOR)
list(APPEND CHDR_DEFINES WANT_RAW_DATA_SECTOR=1)
else()
list(APPEND CHDR_DEFINES WANT_RAW_DATA_SECTOR=0)
endif()
if(CHDR_WANT_SUBCODE)
list(APPEND CHDR_DEFINES WANT_SUBCODE=1)
else()
list(APPEND CHDR_DEFINES WANT_SUBCODE=0)
endif()
if(CHDR_VERIFY_BLOCK_CRC)
list(APPEND CHDR_DEFINES VERIFY_BLOCK_CRC=1)
else()
list(APPEND CHDR_DEFINES VERIFY_BLOCK_CRC=0)
endif()
#--------------------------------------------------
# chdr
#--------------------------------------------------
set(CHDR_SOURCES
src/libchdr_bitstream.c
src/libchdr_cdrom.c
src/libchdr_chd.c
src/libchdr_codec_cdfl.c
src/libchdr_codec_cdlz.c
src/libchdr_codec_cdzl.c
src/libchdr_codec_cdzs.c
src/libchdr_codec_flac.c
src/libchdr_codec_huff.c
src/libchdr_codec_lzma.c
src/libchdr_codec_zlib.c
src/libchdr_codec_zstd.c
src/libchdr_flac.c
src/libchdr_huffman.c
)
add_library(chdr-static STATIC ${CHDR_SOURCES})
target_include_directories(chdr-static INTERFACE include)
target_link_libraries(chdr-static PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
target_compile_definitions(chdr-static PRIVATE ${CHDR_DEFINES})
if(MSVC)
target_compile_definitions(chdr-static PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
if (INSTALL_STATIC_LIBS)
install(TARGETS chdr-static ${CHDR_LIBS}
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
endif()
if (BUILD_SHARED_LIBS)
add_library(chdr SHARED ${CHDR_SOURCES})
target_include_directories(chdr INTERFACE include)
target_link_libraries(chdr PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
target_compile_definitions(chdr PRIVATE ${CHDR_DEFINES})
if(MSVC)
target_compile_definitions(chdr PUBLIC "CHD_DLL")
target_compile_definitions(chdr PRIVATE "CHD_DLL_EXPORTS")
target_compile_definitions(chdr PRIVATE _CRT_SECURE_NO_WARNINGS)
elseif(APPLE)
target_link_libraries(chdr PRIVATE -Wl,-dead_strip -Wl,-exported_symbol,_chd_*)
else()
include(CheckLinkerFlag)
check_linker_flag(C "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/link.T" LINKER_VERSION_SCRIPT_SUPPORTED)
if(LINKER_VERSION_SCRIPT_SUPPORTED)
target_link_options(chdr PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/link.T")
endif()
if(NOT CMAKE_SYSTEM_NAME STREQUAL OpenBSD)
target_link_libraries(chdr PRIVATE -Wl,--no-undefined)
endif()
endif()
set_target_properties(chdr PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(chdr PROPERTIES VISIBILITY_INLINES_HIDDEN 1)
set_target_properties(chdr PROPERTIES PUBLIC_HEADER "include/libchdr/bitstream.h;include/libchdr/cdrom.h;include/libchdr/chd.h;include/libchdr/chdconfig.h;include/libchdr/coretypes.h;include/libchdr/flac.h;include/libchdr/huffman.h;include/libchdr/macros.h")
set_target_properties(chdr PROPERTIES VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}" SOVERSION ${PROJECT_VERSION_MAJOR})
if (CMAKE_BUILD_TYPE MATCHES Release)
#add_custom_command(TARGET chdr POST_BUILD COMMAND ${CMAKE_STRIP} libchdr.so)
endif (CMAKE_BUILD_TYPE MATCHES Release)
install(TARGETS chdr
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libchdr"
)
configure_file(pkg-config.pc.in ${CMAKE_BINARY_DIR}/libchdr.pc @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/libchdr.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
endif()
add_subdirectory(tests)
+1
View File
@@ -0,0 +1 @@
6cde534
+24
View File
@@ -0,0 +1,24 @@
Copyright Romain Tisserand
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+7
View File
@@ -0,0 +1,7 @@
# libchdr
libchdr is a standalone library for reading MAME's CHDv1-v5 formats.
The code is based off of MAME's old C codebase which read up to CHDv4 with OS-dependent features removed, and CHDv5 support backported from MAME's current C++ codebase.
libchdr is licensed under the BSD 3-Clause (see [LICENSE.txt](LICENSE.txt)) and uses third party libraries that are each distributed under their own terms (see each library's license in [deps/](deps/)).
+33
View File
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
# SPDX-License-Identifier: ISC
include(FindPackageHandleStandardArgs)
find_package(zstd CONFIG)
if (zstd_FOUND)
# Use upstream zstdConfig.cmake if possible
if (NOT TARGET zstd::zstd)
if (TARGET zstd::libzstd_static)
add_library(zstd::zstd ALIAS zstd::libzstd_static)
elseif (TARGET zstd::libzstd_shared)
add_library(zstd::zstd ALIAS zstd::libzstd_shared)
endif()
endif()
find_package_handle_standard_args(zstd CONFIG_MODE)
else()
# Fallback to pkg-config otherwise
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(libzstd IMPORTED_TARGET GLOBAL libzstd)
if (libzstd_FOUND)
add_library(zstd::zstd ALIAS PkgConfig::libzstd)
endif()
endif()
find_package_handle_standard_args(zstd
REQUIRED_VARS
libzstd_LINK_LIBRARIES
libzstd_FOUND
VERSION_VAR libzstd_VERSION
)
endif()
+181
View File
@@ -0,0 +1,181 @@
// 7zAsm.S -- ASM macros for arm64
// 2021-04-25 : Igor Pavlov : Public domain
#define r0 x0
#define r1 x1
#define r2 x2
#define r3 x3
#define r4 x4
#define r5 x5
#define r6 x6
#define r7 x7
#define r8 x8
#define r9 x9
#define r10 x10
#define r11 x11
#define r12 x12
#define r13 x13
#define r14 x14
#define r15 x15
#define r16 x16
#define r17 x17
#define r18 x18
#define r19 x19
#define r20 x20
#define r21 x21
#define r22 x22
#define r23 x23
#define r24 x24
#define r25 x25
#define r26 x26
#define r27 x27
#define r28 x28
#define r29 x29
#define r30 x30
#define REG_ABI_PARAM_0 r0
#define REG_ABI_PARAM_1 r1
#define REG_ABI_PARAM_2 r2
.macro p2_add reg:req, param:req
add \reg, \reg, \param
.endm
.macro p2_sub reg:req, param:req
sub \reg, \reg, \param
.endm
.macro p2_sub_s reg:req, param:req
subs \reg, \reg, \param
.endm
.macro p2_and reg:req, param:req
and \reg, \reg, \param
.endm
.macro xor reg:req, param:req
eor \reg, \reg, \param
.endm
.macro or reg:req, param:req
orr \reg, \reg, \param
.endm
.macro shl reg:req, param:req
lsl \reg, \reg, \param
.endm
.macro shr reg:req, param:req
lsr \reg, \reg, \param
.endm
.macro sar reg:req, param:req
asr \reg, \reg, \param
.endm
.macro p1_neg reg:req
neg \reg, \reg
.endm
.macro dec reg:req
sub \reg, \reg, 1
.endm
.macro dec_s reg:req
subs \reg, \reg, 1
.endm
.macro inc reg:req
add \reg, \reg, 1
.endm
.macro inc_s reg:req
adds \reg, \reg, 1
.endm
.macro imul reg:req, param:req
mul \reg, \reg, \param
.endm
/*
arm64 and arm use reverted c flag after subs/cmp instructions:
arm64-arm : x86
b.lo / b.cc : jb / jc
b.hs / b.cs : jae / jnc
*/
.macro jmp lab:req
b \lab
.endm
.macro je lab:req
b.eq \lab
.endm
.macro jz lab:req
b.eq \lab
.endm
.macro jnz lab:req
b.ne \lab
.endm
.macro jne lab:req
b.ne \lab
.endm
.macro jb lab:req
b.lo \lab
.endm
.macro jbe lab:req
b.ls \lab
.endm
.macro ja lab:req
b.hi \lab
.endm
.macro jae lab:req
b.hs \lab
.endm
.macro cmove dest:req, srcTrue:req
csel \dest, \srcTrue, \dest, eq
.endm
.macro cmovne dest:req, srcTrue:req
csel \dest, \srcTrue, \dest, ne
.endm
.macro cmovs dest:req, srcTrue:req
csel \dest, \srcTrue, \dest, mi
.endm
.macro cmovns dest:req, srcTrue:req
csel \dest, \srcTrue, \dest, pl
.endm
.macro cmovb dest:req, srcTrue:req
csel \dest, \srcTrue, \dest, lo
.endm
.macro cmovae dest:req, srcTrue:req
csel \dest, \srcTrue, \dest, hs
.endm
.macro MY_ALIGN_16 macro
.p2align 4,, (1 << 4) - 1
.endm
.macro MY_ALIGN_32 macro
.p2align 5,, (1 << 5) - 1
.endm
.macro MY_ALIGN_64 macro
.p2align 6,, (1 << 6) - 1
.endm
File diff suppressed because it is too large. Load diff
+341
View File
@@ -0,0 +1,341 @@
; 7zAsm.asm -- ASM macros
; 2023-12-08 : Igor Pavlov : Public domain
; UASM can require these changes
; OPTION FRAMEPRESERVEFLAGS:ON
; OPTION PROLOGUE:NONE
; OPTION EPILOGUE:NONE
ifdef @wordsize
; @wordsize is defined only in JWASM and ASMC and is not defined in MASM
; @wordsize eq 8 for 64-bit x64
; @wordsize eq 2 for 32-bit x86
if @wordsize eq 8
x64 equ 1
endif
else
ifdef RAX
x64 equ 1
endif
endif
ifdef x64
IS_X64 equ 1
else
IS_X64 equ 0
endif
ifdef ABI_LINUX
IS_LINUX equ 1
else
IS_LINUX equ 0
endif
ifndef x64
; Use ABI_CDECL for x86 (32-bit) only
; if ABI_CDECL is not defined, we use fastcall abi
ifdef ABI_CDECL
IS_CDECL equ 1
else
IS_CDECL equ 0
endif
endif
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE
MY_ASM_START macro
ifdef x64
.code
else
.386
.model flat
_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
endif
endm
MY_PROC macro name:req, numParams:req
align 16
proc_numParams = numParams
if (IS_X64 gt 0)
proc_name equ name
elseif (IS_LINUX gt 0)
proc_name equ name
elseif (IS_CDECL gt 0)
proc_name equ @CatStr(_,name)
else
proc_name equ @CatStr(@,name,@, %numParams * 4)
endif
proc_name PROC
endm
MY_ENDP macro
if (IS_X64 gt 0)
ret
elseif (IS_CDECL gt 0)
ret
elseif (proc_numParams LT 3)
ret
else
ret (proc_numParams - 2) * 4
endif
proc_name ENDP
endm
ifdef x64
REG_SIZE equ 8
REG_LOGAR_SIZE equ 3
else
REG_SIZE equ 4
REG_LOGAR_SIZE equ 2
endif
x0 equ EAX
x1 equ ECX
x2 equ EDX
x3 equ EBX
x4 equ ESP
x5 equ EBP
x6 equ ESI
x7 equ EDI
x0_W equ AX
x1_W equ CX
x2_W equ DX
x3_W equ BX
x5_W equ BP
x6_W equ SI
x7_W equ DI
x0_L equ AL
x1_L equ CL
x2_L equ DL
x3_L equ BL
x0_H equ AH
x1_H equ CH
x2_H equ DH
x3_H equ BH
; r0_L equ AL
; r1_L equ CL
; r2_L equ DL
; r3_L equ BL
; r0_H equ AH
; r1_H equ CH
; r2_H equ DH
; r3_H equ BH
ifdef x64
x5_L equ BPL
x6_L equ SIL
x7_L equ DIL
x8_L equ r8b
x9_L equ r9b
x10_L equ r10b
x11_L equ r11b
x12_L equ r12b
x13_L equ r13b
x14_L equ r14b
x15_L equ r15b
r0 equ RAX
r1 equ RCX
r2 equ RDX
r3 equ RBX
r4 equ RSP
r5 equ RBP
r6 equ RSI
r7 equ RDI
x8 equ r8d
x9 equ r9d
x10 equ r10d
x11 equ r11d
x12 equ r12d
x13 equ r13d
x14 equ r14d
x15 equ r15d
else
r0 equ x0
r1 equ x1
r2 equ x2
r3 equ x3
r4 equ x4
r5 equ x5
r6 equ x6
r7 equ x7
endif
x0_R equ r0
x1_R equ r1
x2_R equ r2
x3_R equ r3
x4_R equ r4
x5_R equ r5
x6_R equ r6
x7_R equ r7
x8_R equ r8
x9_R equ r9
x10_R equ r10
x11_R equ r11
x12_R equ r12
x13_R equ r13
x14_R equ r14
x15_R equ r15
ifdef x64
ifdef ABI_LINUX
MY_PUSH_2_REGS macro
push r3
push r5
endm
MY_POP_2_REGS macro
pop r5
pop r3
endm
endif
endif
MY_PUSH_4_REGS macro
push r3
push r5
push r6
push r7
endm
MY_POP_4_REGS macro
pop r7
pop r6
pop r5
pop r3
endm
; for fastcall and for WIN-x64
REG_PARAM_0_x equ x1
REG_PARAM_0 equ r1
REG_PARAM_1_x equ x2
REG_PARAM_1 equ r2
ifndef x64
; for x86-fastcall
REG_ABI_PARAM_0_x equ REG_PARAM_0_x
REG_ABI_PARAM_0 equ REG_PARAM_0
REG_ABI_PARAM_1_x equ REG_PARAM_1_x
REG_ABI_PARAM_1 equ REG_PARAM_1
MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 macro
MY_PUSH_4_REGS
endm
MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 macro
MY_POP_4_REGS
endm
else
; x64
if (IS_LINUX eq 0)
; for WIN-x64:
REG_PARAM_2_x equ x8
REG_PARAM_2 equ r8
REG_PARAM_3 equ r9
REG_ABI_PARAM_0_x equ REG_PARAM_0_x
REG_ABI_PARAM_0 equ REG_PARAM_0
REG_ABI_PARAM_1_x equ REG_PARAM_1_x
REG_ABI_PARAM_1 equ REG_PARAM_1
REG_ABI_PARAM_2_x equ REG_PARAM_2_x
REG_ABI_PARAM_2 equ REG_PARAM_2
REG_ABI_PARAM_3 equ REG_PARAM_3
else
; for LINUX-x64:
REG_LINUX_PARAM_0_x equ x7
REG_LINUX_PARAM_0 equ r7
REG_LINUX_PARAM_1_x equ x6
REG_LINUX_PARAM_1 equ r6
REG_LINUX_PARAM_2 equ r2
REG_LINUX_PARAM_3 equ r1
REG_LINUX_PARAM_4_x equ x8
REG_LINUX_PARAM_4 equ r8
REG_LINUX_PARAM_5 equ r9
REG_ABI_PARAM_0_x equ REG_LINUX_PARAM_0_x
REG_ABI_PARAM_0 equ REG_LINUX_PARAM_0
REG_ABI_PARAM_1_x equ REG_LINUX_PARAM_1_x
REG_ABI_PARAM_1 equ REG_LINUX_PARAM_1
REG_ABI_PARAM_2 equ REG_LINUX_PARAM_2
REG_ABI_PARAM_3 equ REG_LINUX_PARAM_3
REG_ABI_PARAM_4_x equ REG_LINUX_PARAM_4_x
REG_ABI_PARAM_4 equ REG_LINUX_PARAM_4
REG_ABI_PARAM_5 equ REG_LINUX_PARAM_5
MY_ABI_LINUX_TO_WIN_2 macro
mov r2, r6
mov r1, r7
endm
MY_ABI_LINUX_TO_WIN_3 macro
mov r8, r2
mov r2, r6
mov r1, r7
endm
MY_ABI_LINUX_TO_WIN_4 macro
mov r9, r1
mov r8, r2
mov r2, r6
mov r1, r7
endm
endif ; IS_LINUX
MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 macro
if (IS_LINUX gt 0)
MY_PUSH_2_REGS
else
MY_PUSH_4_REGS
endif
endm
MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11 macro
if (IS_LINUX gt 0)
MY_POP_2_REGS
else
MY_POP_4_REGS
endif
endm
MY_PUSH_PRESERVED_ABI_REGS macro
MY_PUSH_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11
push r12
push r13
push r14
push r15
endm
MY_POP_PRESERVED_ABI_REGS macro
pop r15
pop r14
pop r13
pop r12
MY_POP_PRESERVED_ABI_REGS_UP_TO_INCLUDING_R11
endm
endif ; x64
File diff suppressed because it is too large. Load diff
+29
View File
@@ -0,0 +1,29 @@
add_library(chdr-lzma STATIC
include/LzmaDec.h
src/LzmaDec.c
)
set_target_properties(chdr-lzma PROPERTIES POSITION_INDEPENDENT_CODE ON)
option(WITH_LZMA_ASM "Use lzma asm" ON)
if(WITH_LZMA_ASM)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
include(CheckSymbolExists)
check_symbol_exists("__aarch64__" "" CPU_ARM64)
if(CPU_ARM64)
enable_language(ASM)
set_source_files_properties(src/LzmaDec.c PROPERTIES COMPILE_DEFINITIONS Z7_LZMA_DEC_OPT)
target_sources(chdr-lzma PRIVATE Asm/arm64/LzmaDecOpt.S)
set_source_files_properties(Asm/arm64/LzmaDecOpt.S PROPERTIES LANGUAGE ASM)
endif()
elseif(WIN32)
include(CheckSymbolExists)
check_symbol_exists("_M_AMD64" "" CPU_X64)
if(CPU_X64)
enable_language(ASM_MASM)
set_source_files_properties(src/LzmaDec.c PROPERTIES COMPILE_DEFINITIONS Z7_LZMA_DEC_OPT)
target_sources(chdr-lzma PRIVATE Asm/x86/LzmaDecOpt.asm)
set_source_files_properties(Asm/x86/LzmaDecOpt.asm PROPERTIES LANGUAGE ASM_MASM)
endif()
endif()
endif()
+3
View File
@@ -0,0 +1,3 @@
LZMA SDK is placed in the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute the original LZMA SDK code, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
+13
View File
@@ -0,0 +1,13 @@
/* Namespace some symbols to avoid linker errors in static libretro builds. */
#define LzmaDec_InitDicAndState CHDR_LzmaDec_InitDicAndState
#define LzmaDec_Init CHDR_LzmaDec_Init
#define LzmaDec_DecodeToDic CHDR_LzmaDec_DecodeToDic
#define LzmaDec_DecodeToBuf CHDR_LzmaDec_DecodeToBuf
#define LzmaDec_FreeProbs CHDR_LzmaDec_FreeProbs
#define LzmaDec_Free CHDR_LzmaDec_Free
#define LzmaProps_Decode CHDR_LzmaProps_Decode
#define LzmaDec_AllocateProbs CHDR_LzmaDec_AllocateProbs
#define LzmaDec_Allocate CHDR_LzmaDec_Allocate
#define LzmaDecode CHDR_LzmaDecode
#include "real/LzmaDec.h"
+597
View File
@@ -0,0 +1,597 @@
/* 7zTypes.h -- Basic types
2024-01-24 : Igor Pavlov : Public domain */
#ifndef ZIP7_7Z_TYPES_H
#define ZIP7_7Z_TYPES_H
#ifdef _WIN32
/* #include <windows.h> */
#else
#include <errno.h>
#endif
#include <stddef.h>
#ifndef EXTERN_C_BEGIN
#ifdef __cplusplus
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C_BEGIN
#define EXTERN_C_END
#endif
#endif
EXTERN_C_BEGIN
#define SZ_OK 0
#define SZ_ERROR_DATA 1
#define SZ_ERROR_MEM 2
#define SZ_ERROR_CRC 3
#define SZ_ERROR_UNSUPPORTED 4
#define SZ_ERROR_PARAM 5
#define SZ_ERROR_INPUT_EOF 6
#define SZ_ERROR_OUTPUT_EOF 7
#define SZ_ERROR_READ 8
#define SZ_ERROR_WRITE 9
#define SZ_ERROR_PROGRESS 10
#define SZ_ERROR_FAIL 11
#define SZ_ERROR_THREAD 12
#define SZ_ERROR_ARCHIVE 16
#define SZ_ERROR_NO_ARCHIVE 17
typedef int SRes;
#ifdef _MSC_VER
#if _MSC_VER > 1200
#define MY_ALIGN(n) __declspec(align(n))
#else
#define MY_ALIGN(n)
#endif
#else
/*
// C11/C++11:
#include <stdalign.h>
#define MY_ALIGN(n) alignas(n)
*/
#define MY_ALIGN(n) __attribute__ ((aligned(n)))
#endif
#ifdef _WIN32
/* typedef DWORD WRes; */
typedef unsigned WRes;
#define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x)
// #define MY_HRES_ERROR_INTERNAL_ERROR MY_SRes_HRESULT_FROM_WRes(ERROR_INTERNAL_ERROR)
#else // _WIN32
// #define ENV_HAVE_LSTAT
typedef int WRes;
// (FACILITY_ERRNO = 0x800) is 7zip's FACILITY constant to represent (errno) errors in HRESULT
#define MY_FACILITY_ERRNO 0x800
#define MY_FACILITY_WIN32 7
#define MY_FACILITY_WRes MY_FACILITY_ERRNO
#define MY_HRESULT_FROM_errno_CONST_ERROR(x) ((HRESULT)( \
( (HRESULT)(x) & 0x0000FFFF) \
| (MY_FACILITY_WRes << 16) \
| (HRESULT)0x80000000 ))
#define MY_SRes_HRESULT_FROM_WRes(x) \
((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : MY_HRESULT_FROM_errno_CONST_ERROR(x))
// we call macro HRESULT_FROM_WIN32 for system errors (WRes) that are (errno)
#define HRESULT_FROM_WIN32(x) MY_SRes_HRESULT_FROM_WRes(x)
/*
#define ERROR_FILE_NOT_FOUND 2L
#define ERROR_ACCESS_DENIED 5L
#define ERROR_NO_MORE_FILES 18L
#define ERROR_LOCK_VIOLATION 33L
#define ERROR_FILE_EXISTS 80L
#define ERROR_DISK_FULL 112L
#define ERROR_NEGATIVE_SEEK 131L
#define ERROR_ALREADY_EXISTS 183L
#define ERROR_DIRECTORY 267L
#define ERROR_TOO_MANY_POSTS 298L
#define ERROR_INTERNAL_ERROR 1359L
#define ERROR_INVALID_REPARSE_DATA 4392L
#define ERROR_REPARSE_TAG_INVALID 4393L
#define ERROR_REPARSE_TAG_MISMATCH 4394L
*/
// we use errno equivalents for some WIN32 errors:
#define ERROR_INVALID_PARAMETER EINVAL
#define ERROR_INVALID_FUNCTION EINVAL
#define ERROR_ALREADY_EXISTS EEXIST
#define ERROR_FILE_EXISTS EEXIST
#define ERROR_PATH_NOT_FOUND ENOENT
#define ERROR_FILE_NOT_FOUND ENOENT
#define ERROR_DISK_FULL ENOSPC
// #define ERROR_INVALID_HANDLE EBADF
// we use FACILITY_WIN32 for errors that has no errno equivalent
// Too many posts were made to a semaphore.
#define ERROR_TOO_MANY_POSTS ((HRESULT)0x8007012AL)
#define ERROR_INVALID_REPARSE_DATA ((HRESULT)0x80071128L)
#define ERROR_REPARSE_TAG_INVALID ((HRESULT)0x80071129L)
// if (MY_FACILITY_WRes != FACILITY_WIN32),
// we use FACILITY_WIN32 for COM errors:
#define E_OUTOFMEMORY ((HRESULT)0x8007000EL)
#define E_INVALIDARG ((HRESULT)0x80070057L)
#define MY_E_ERROR_NEGATIVE_SEEK ((HRESULT)0x80070083L)
/*
// we can use FACILITY_ERRNO for some COM errors, that have errno equivalents:
#define E_OUTOFMEMORY MY_HRESULT_FROM_errno_CONST_ERROR(ENOMEM)
#define E_INVALIDARG MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL)
#define MY_E_ERROR_NEGATIVE_SEEK MY_HRESULT_FROM_errno_CONST_ERROR(EINVAL)
*/
#define TEXT(quote) quote
#define FILE_ATTRIBUTE_READONLY 0x0001
#define FILE_ATTRIBUTE_HIDDEN 0x0002
#define FILE_ATTRIBUTE_SYSTEM 0x0004
#define FILE_ATTRIBUTE_DIRECTORY 0x0010
#define FILE_ATTRIBUTE_ARCHIVE 0x0020
#define FILE_ATTRIBUTE_DEVICE 0x0040
#define FILE_ATTRIBUTE_NORMAL 0x0080
#define FILE_ATTRIBUTE_TEMPORARY 0x0100
#define FILE_ATTRIBUTE_SPARSE_FILE 0x0200
#define FILE_ATTRIBUTE_REPARSE_POINT 0x0400
#define FILE_ATTRIBUTE_COMPRESSED 0x0800
#define FILE_ATTRIBUTE_OFFLINE 0x1000
#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x2000
#define FILE_ATTRIBUTE_ENCRYPTED 0x4000
#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
#endif
#ifndef RINOK
#define RINOK(x) { const int _result_ = (x); if (_result_ != 0) return _result_; }
#endif
#ifndef RINOK_WRes
#define RINOK_WRes(x) { const WRes _result_ = (x); if (_result_ != 0) return _result_; }
#endif
typedef unsigned char Byte;
typedef short Int16;
typedef unsigned short UInt16;
#ifdef Z7_DECL_Int32_AS_long
typedef long Int32;
typedef unsigned long UInt32;
#else
typedef int Int32;
typedef unsigned int UInt32;
#endif
#ifndef _WIN32
typedef int INT;
typedef Int32 INT32;
typedef unsigned int UINT;
typedef UInt32 UINT32;
typedef INT32 LONG; // LONG, ULONG and DWORD must be 32-bit for _WIN32 compatibility
typedef UINT32 ULONG;
#undef DWORD
typedef UINT32 DWORD;
#define VOID void
#define HRESULT LONG
typedef void *LPVOID;
// typedef void VOID;
// typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
// gcc / clang on Unix : sizeof(long==sizeof(void*) in 32 or 64 bits)
typedef long INT_PTR;
typedef unsigned long UINT_PTR;
typedef long LONG_PTR;
typedef unsigned long DWORD_PTR;
typedef size_t SIZE_T;
#endif // _WIN32
#define MY_HRES_ERROR_INTERNAL_ERROR ((HRESULT)0x8007054FL)
#ifdef Z7_DECL_Int64_AS_long
typedef long Int64;
typedef unsigned long UInt64;
#else
#if (defined(_MSC_VER) || defined(__BORLANDC__)) && !defined(__clang__)
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else
#if defined(__clang__) || defined(__GNUC__)
#include <stdint.h>
typedef int64_t Int64;
typedef uint64_t UInt64;
#else
typedef long long int Int64;
typedef unsigned long long int UInt64;
// #define UINT64_CONST(n) n ## ULL
#endif
#endif
#endif
#define UINT64_CONST(n) n
#ifdef Z7_DECL_SizeT_AS_unsigned_int
typedef unsigned int SizeT;
#else
typedef size_t SizeT;
#endif
/*
#if (defined(_MSC_VER) && _MSC_VER <= 1200)
typedef size_t MY_uintptr_t;
#else
#include <stdint.h>
typedef uintptr_t MY_uintptr_t;
#endif
*/
typedef int BoolInt;
/* typedef BoolInt Bool; */
#define True 1
#define False 0
#ifdef _WIN32
#define Z7_STDCALL __stdcall
#else
#define Z7_STDCALL
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1300
#define Z7_NO_INLINE __declspec(noinline)
#else
#define Z7_NO_INLINE
#endif
#define Z7_FORCE_INLINE __forceinline
#define Z7_CDECL __cdecl
#define Z7_FASTCALL __fastcall
#else // _MSC_VER
#if (defined(__GNUC__) && (__GNUC__ >= 4)) \
|| (defined(__clang__) && (__clang_major__ >= 4)) \
|| defined(__INTEL_COMPILER) \
|| defined(__xlC__)
#define Z7_NO_INLINE __attribute__((noinline))
#define Z7_FORCE_INLINE __attribute__((always_inline)) inline
#else
#define Z7_NO_INLINE
#define Z7_FORCE_INLINE
#endif
#define Z7_CDECL
#if defined(_M_IX86) \
|| defined(__i386__)
// #define Z7_FASTCALL __attribute__((fastcall))
// #define Z7_FASTCALL __attribute__((cdecl))
#define Z7_FASTCALL
#elif defined(MY_CPU_AMD64)
// #define Z7_FASTCALL __attribute__((ms_abi))
#define Z7_FASTCALL
#else
#define Z7_FASTCALL
#endif
#endif // _MSC_VER
/* The following interfaces use first parameter as pointer to structure */
// #define Z7_C_IFACE_CONST_QUAL
#define Z7_C_IFACE_CONST_QUAL const
#define Z7_C_IFACE_DECL(a) \
struct a ## _; \
typedef Z7_C_IFACE_CONST_QUAL struct a ## _ * a ## Ptr; \
typedef struct a ## _ a; \
struct a ## _
Z7_C_IFACE_DECL (IByteIn)
{
Byte (*Read)(IByteInPtr p); /* reads one byte, returns 0 in case of EOF or error */
};
#define IByteIn_Read(p) (p)->Read(p)
Z7_C_IFACE_DECL (IByteOut)
{
void (*Write)(IByteOutPtr p, Byte b);
};
#define IByteOut_Write(p, b) (p)->Write(p, b)
Z7_C_IFACE_DECL (ISeqInStream)
{
SRes (*Read)(ISeqInStreamPtr p, void *buf, size_t *size);
/* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
(output(*size) < input(*size)) is allowed */
};
#define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size)
/* try to read as much as avail in stream and limited by (*processedSize) */
SRes SeqInStream_ReadMax(ISeqInStreamPtr stream, void *buf, size_t *processedSize);
/* it can return SZ_ERROR_INPUT_EOF */
// SRes SeqInStream_Read(ISeqInStreamPtr stream, void *buf, size_t size);
// SRes SeqInStream_Read2(ISeqInStreamPtr stream, void *buf, size_t size, SRes errorType);
SRes SeqInStream_ReadByte(ISeqInStreamPtr stream, Byte *buf);
Z7_C_IFACE_DECL (ISeqOutStream)
{
size_t (*Write)(ISeqOutStreamPtr p, const void *buf, size_t size);
/* Returns: result - the number of actually written bytes.
(result < size) means error */
};
#define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size)
typedef enum
{
SZ_SEEK_SET = 0,
SZ_SEEK_CUR = 1,
SZ_SEEK_END = 2
} ESzSeek;
Z7_C_IFACE_DECL (ISeekInStream)
{
SRes (*Read)(ISeekInStreamPtr p, void *buf, size_t *size); /* same as ISeqInStream::Read */
SRes (*Seek)(ISeekInStreamPtr p, Int64 *pos, ESzSeek origin);
};
#define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size)
#define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
Z7_C_IFACE_DECL (ILookInStream)
{
SRes (*Look)(ILookInStreamPtr p, const void **buf, size_t *size);
/* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
(output(*size) > input(*size)) is not allowed
(output(*size) < input(*size)) is allowed */
SRes (*Skip)(ILookInStreamPtr p, size_t offset);
/* offset must be <= output(*size) of Look */
SRes (*Read)(ILookInStreamPtr p, void *buf, size_t *size);
/* reads directly (without buffer). It's same as ISeqInStream::Read */
SRes (*Seek)(ILookInStreamPtr p, Int64 *pos, ESzSeek origin);
};
#define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size)
#define ILookInStream_Skip(p, offset) (p)->Skip(p, offset)
#define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size)
#define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
SRes LookInStream_LookRead(ILookInStreamPtr stream, void *buf, size_t *size);
SRes LookInStream_SeekTo(ILookInStreamPtr stream, UInt64 offset);
/* reads via ILookInStream::Read */
SRes LookInStream_Read2(ILookInStreamPtr stream, void *buf, size_t size, SRes errorType);
SRes LookInStream_Read(ILookInStreamPtr stream, void *buf, size_t size);
typedef struct
{
ILookInStream vt;
ISeekInStreamPtr realStream;
size_t pos;
size_t size; /* it's data size */
/* the following variables must be set outside */
Byte *buf;
size_t bufSize;
} CLookToRead2;
void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead);
#define LookToRead2_INIT(p) { (p)->pos = (p)->size = 0; }
typedef struct
{
ISeqInStream vt;
ILookInStreamPtr realStream;
} CSecToLook;
void SecToLook_CreateVTable(CSecToLook *p);
typedef struct
{
ISeqInStream vt;
ILookInStreamPtr realStream;
} CSecToRead;
void SecToRead_CreateVTable(CSecToRead *p);
Z7_C_IFACE_DECL (ICompressProgress)
{
SRes (*Progress)(ICompressProgressPtr p, UInt64 inSize, UInt64 outSize);
/* Returns: result. (result != SZ_OK) means break.
Value (UInt64)(Int64)-1 for size means unknown value. */
};
#define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize)
typedef struct ISzAlloc ISzAlloc;
typedef const ISzAlloc * ISzAllocPtr;
struct ISzAlloc
{
void *(*Alloc)(ISzAllocPtr p, size_t size);
void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
};
#define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size)
#define ISzAlloc_Free(p, a) (p)->Free(p, a)
/* deprecated */
#define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size)
#define IAlloc_Free(p, a) ISzAlloc_Free(p, a)
#ifndef MY_offsetof
#ifdef offsetof
#define MY_offsetof(type, m) offsetof(type, m)
/*
#define MY_offsetof(type, m) FIELD_OFFSET(type, m)
*/
#else
#define MY_offsetof(type, m) ((size_t)&(((type *)0)->m))
#endif
#endif
#ifndef Z7_container_of
/*
#define Z7_container_of(ptr, type, m) container_of(ptr, type, m)
#define Z7_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m)
#define Z7_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m)))
#define Z7_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m))))
*/
/*
GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly"
GCC 3.4.4 : classes with constructor
GCC 4.8.1 : classes with non-public variable members"
*/
#define Z7_container_of(ptr, type, m) \
((type *)(void *)((char *)(void *) \
(1 ? (ptr) : &((type *)NULL)->m) - MY_offsetof(type, m)))
#define Z7_container_of_CONST(ptr, type, m) \
((const type *)(const void *)((const char *)(const void *) \
(1 ? (ptr) : &((type *)NULL)->m) - MY_offsetof(type, m)))
/*
#define Z7_container_of_NON_CONST_FROM_CONST(ptr, type, m) \
((type *)(void *)(const void *)((const char *)(const void *) \
(1 ? (ptr) : &((type *)NULL)->m) - MY_offsetof(type, m)))
*/
#endif
#define Z7_CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(void *)(ptr))
// #define Z7_CONTAINER_FROM_VTBL(ptr, type, m) Z7_CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
#define Z7_CONTAINER_FROM_VTBL(ptr, type, m) Z7_container_of(ptr, type, m)
// #define Z7_CONTAINER_FROM_VTBL(ptr, type, m) Z7_container_of_NON_CONST_FROM_CONST(ptr, type, m)
#define Z7_CONTAINER_FROM_VTBL_CONST(ptr, type, m) Z7_container_of_CONST(ptr, type, m)
#define Z7_CONTAINER_FROM_VTBL_CLS(ptr, type, m) Z7_CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
/*
#define Z7_CONTAINER_FROM_VTBL_CLS(ptr, type, m) Z7_CONTAINER_FROM_VTBL(ptr, type, m)
*/
#if defined (__clang__) || defined(__GNUC__)
#define Z7_DIAGNOSTIC_IGNORE_BEGIN_CAST_QUAL \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
#define Z7_DIAGNOSTIC_IGNORE_END_CAST_QUAL \
_Pragma("GCC diagnostic pop")
#else
#define Z7_DIAGNOSTIC_IGNORE_BEGIN_CAST_QUAL
#define Z7_DIAGNOSTIC_IGNORE_END_CAST_QUAL
#endif
#define Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR(ptr, type, m, p) \
Z7_DIAGNOSTIC_IGNORE_BEGIN_CAST_QUAL \
type *p = Z7_CONTAINER_FROM_VTBL(ptr, type, m); \
Z7_DIAGNOSTIC_IGNORE_END_CAST_QUAL
#define Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR_pp_vt_p(type) \
Z7_CONTAINER_FROM_VTBL_TO_DECL_VAR(pp, type, vt, p)
// #define ZIP7_DECLARE_HANDLE(name) typedef void *name;
#define Z7_DECLARE_HANDLE(name) struct name##_dummy{int unused;}; typedef struct name##_dummy *name;
#define Z7_memset_0_ARRAY(a) memset((a), 0, sizeof(a))
#ifndef Z7_ARRAY_SIZE
#define Z7_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
#ifdef _WIN32
#define CHAR_PATH_SEPARATOR '\\'
#define WCHAR_PATH_SEPARATOR L'\\'
#define STRING_PATH_SEPARATOR "\\"
#define WSTRING_PATH_SEPARATOR L"\\"
#else
#define CHAR_PATH_SEPARATOR '/'
#define WCHAR_PATH_SEPARATOR L'/'
#define STRING_PATH_SEPARATOR "/"
#define WSTRING_PATH_SEPARATOR L"/"
#endif
#define k_PropVar_TimePrec_0 0
#define k_PropVar_TimePrec_Unix 1
#define k_PropVar_TimePrec_DOS 2
#define k_PropVar_TimePrec_HighPrec 3
#define k_PropVar_TimePrec_Base 16
#define k_PropVar_TimePrec_100ns (k_PropVar_TimePrec_Base + 7)
#define k_PropVar_TimePrec_1ns (k_PropVar_TimePrec_Base + 9)
EXTERN_C_END
#endif
/*
#ifndef Z7_ST
#ifdef _7ZIP_ST
#define Z7_ST
#endif
#endif
*/
+237
View File
@@ -0,0 +1,237 @@
/* LzmaDec.h -- LZMA Decoder
2023-04-02 : Igor Pavlov : Public domain */
#ifndef ZIP7_INC_LZMA_DEC_H
#define ZIP7_INC_LZMA_DEC_H
#include "7zTypes.h"
EXTERN_C_BEGIN
/* #define Z7_LZMA_PROB32 */
/* Z7_LZMA_PROB32 can increase the speed on some CPUs,
but memory usage for CLzmaDec::probs will be doubled in that case */
typedef
#ifdef Z7_LZMA_PROB32
UInt32
#else
UInt16
#endif
CLzmaProb;
/* ---------- LZMA Properties ---------- */
#define LZMA_PROPS_SIZE 5
typedef struct
{
Byte lc;
Byte lp;
Byte pb;
Byte _pad_;
UInt32 dicSize;
} CLzmaProps;
/* LzmaProps_Decode - decodes properties
Returns:
SZ_OK
SZ_ERROR_UNSUPPORTED - Unsupported properties
*/
SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
/* ---------- LZMA Decoder state ---------- */
/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
#define LZMA_REQUIRED_INPUT_MAX 20
typedef struct
{
/* Don't change this structure. ASM code can use it. */
CLzmaProps prop;
CLzmaProb *probs;
CLzmaProb *probs_1664;
Byte *dic;
SizeT dicBufSize;
SizeT dicPos;
const Byte *buf;
UInt32 range;
UInt32 code;
UInt32 processedPos;
UInt32 checkDicSize;
UInt32 reps[4];
UInt32 state;
UInt32 remainLen;
UInt32 numProbs;
unsigned tempBufSize;
Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
} CLzmaDec;
#define LzmaDec_CONSTRUCT(p) { (p)->dic = NULL; (p)->probs = NULL; }
#define LzmaDec_Construct(p) LzmaDec_CONSTRUCT(p)
void LzmaDec_Init(CLzmaDec *p);
/* There are two types of LZMA streams:
- Stream with end mark. That end mark adds about 6 bytes to compressed size.
- Stream without end mark. You must know exact uncompressed size to decompress such stream. */
typedef enum
{
LZMA_FINISH_ANY, /* finish at any point */
LZMA_FINISH_END /* block must be finished at the end */
} ELzmaFinishMode;
/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
You must use LZMA_FINISH_END, when you know that current output buffer
covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
and output value of destLen will be less than output buffer size limit.
You can check status result also.
You can use multiple checks to test data integrity after full decompression:
1) Check Result and "status" variable.
2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
You must use correct finish mode in that case. */
typedef enum
{
LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
} ELzmaStatus;
/* ELzmaStatus is used only as output value for function call */
/* ---------- Interfaces ---------- */
/* There are 3 levels of interfaces:
1) Dictionary Interface
2) Buffer Interface
3) One Call Interface
You can select any of these interfaces, but don't mix functions from different
groups for same object. */
/* There are two variants to allocate state for Dictionary Interface:
1) LzmaDec_Allocate / LzmaDec_Free
2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
You can use variant 2, if you set dictionary buffer manually.
For Buffer Interface you must always use variant 1.
LzmaDec_Allocate* can return:
SZ_OK
SZ_ERROR_MEM - Memory allocation error
SZ_ERROR_UNSUPPORTED - Unsupported properties
*/
SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc);
void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc);
SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc);
void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc);
/* ---------- Dictionary Interface ---------- */
/* You can use it, if you want to eliminate the overhead for data copying from
dictionary to some other external buffer.
You must work with CLzmaDec variables directly in this interface.
STEPS:
LzmaDec_Construct()
LzmaDec_Allocate()
for (each new stream)
{
LzmaDec_Init()
while (it needs more decompression)
{
LzmaDec_DecodeToDic()
use data from CLzmaDec::dic and update CLzmaDec::dicPos
}
}
LzmaDec_Free()
*/
/* LzmaDec_DecodeToDic
The decoding to internal dictionary buffer (CLzmaDec::dic).
You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
finishMode:
It has meaning only if the decoding reaches output limit (dicLimit).
LZMA_FINISH_ANY - Decode just dicLimit bytes.
LZMA_FINISH_END - Stream must be finished after dicLimit.
Returns:
SZ_OK
status:
LZMA_STATUS_FINISHED_WITH_MARK
LZMA_STATUS_NOT_FINISHED
LZMA_STATUS_NEEDS_MORE_INPUT
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
SZ_ERROR_DATA - Data error
SZ_ERROR_FAIL - Some unexpected error: internal error of code, memory corruption or hardware failure
*/
SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
/* ---------- Buffer Interface ---------- */
/* It's zlib-like interface.
See LzmaDec_DecodeToDic description for information about STEPS and return results,
but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
to work with CLzmaDec variables manually.
finishMode:
It has meaning only if the decoding reaches output limit (*destLen).
LZMA_FINISH_ANY - Decode just destLen bytes.
LZMA_FINISH_END - Stream must be finished after (*destLen).
*/
SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
/* ---------- One Call Interface ---------- */
/* LzmaDecode
finishMode:
It has meaning only if the decoding reaches output limit (*destLen).
LZMA_FINISH_ANY - Decode just destLen bytes.
LZMA_FINISH_END - Stream must be finished after (*destLen).
Returns:
SZ_OK
status:
LZMA_STATUS_FINISHED_WITH_MARK
LZMA_STATUS_NOT_FINISHED
LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
SZ_ERROR_DATA - Data error
SZ_ERROR_MEM - Memory allocation error
SZ_ERROR_UNSUPPORTED - Unsupported properties
SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
SZ_ERROR_FAIL - Some unexpected error: internal error of code, memory corruption or hardware failure
*/
SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
ELzmaStatus *status, ISzAllocPtr alloc);
EXTERN_C_END
#endif
+2
View File
@@ -0,0 +1,2 @@
#include "../include/LzmaDec.h"
#include "real/LzmaDec.c"
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
option(MINIZ_ARCHIVE_APIS "Enable miniz's ZIP file API" OFF)
option(MINIZ_DEFLATE_APIS "Enable miniz's compression API" OFF)
option(MINIZ_STDIO "Enable miniz's usage of file IO APIs" OFF)
option(MINIZ_TIME "Enable miniz's usage of time APIs" OFF)
add_library(miniz STATIC
miniz.c
miniz.h
)
set_target_properties(miniz PROPERTIES POSITION_INDEPENDENT_CODE ON)
if(NOT MINIZ_ARCHIVE_APIS)
target_compile_definitions(miniz PUBLIC MINIZ_NO_ARCHIVE_APIS)
endif()
if(NOT MINIZ_DEFLATE_APIS)
target_compile_definitions(miniz PUBLIC MINIZ_NO_DEFLATE_APIS)
endif()
if(NOT MINIZ_STDIO)
target_compile_definitions(miniz PUBLIC MINIZ_NO_STDIO)
endif()
if(NOT MINIZ_TIME)
target_compile_definitions(miniz PUBLIC MINIZ_NO_TIME)
endif()
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+7
View File
@@ -0,0 +1,7 @@
add_library(zstd STATIC
zstd.h
zstd_errors.h
zstddeclib.c
)
set_target_properties(zstd PROPERTIES POSITION_INDEPENDENT_CODE ON)
File diff suppressed because it is too large. Load diff
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
* in the COPYING file in the root directory of this source tree).
* You may select, at your option, one of the above-listed licenses.
*/
#ifndef ZSTD_ERRORS_H_398273423
#define ZSTD_ERRORS_H_398273423
#if defined (__cplusplus)
extern "C" {
#endif
/* ===== ZSTDERRORLIB_API : control library symbols visibility ===== */
#ifndef ZSTDERRORLIB_VISIBLE
/* Backwards compatibility with old macro name */
# ifdef ZSTDERRORLIB_VISIBILITY
# define ZSTDERRORLIB_VISIBLE ZSTDERRORLIB_VISIBILITY
# elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
# define ZSTDERRORLIB_VISIBLE __attribute__ ((visibility ("default")))
# else
# define ZSTDERRORLIB_VISIBLE
# endif
#endif
#ifndef ZSTDERRORLIB_HIDDEN
# if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__)
# define ZSTDERRORLIB_HIDDEN __attribute__ ((visibility ("hidden")))
# else
# define ZSTDERRORLIB_HIDDEN
# endif
#endif
#if defined(ZSTD_DLL_EXPORT) && (ZSTD_DLL_EXPORT==1)
# define ZSTDERRORLIB_API __declspec(dllexport) ZSTDERRORLIB_VISIBLE
#elif defined(ZSTD_DLL_IMPORT) && (ZSTD_DLL_IMPORT==1)
# define ZSTDERRORLIB_API __declspec(dllimport) ZSTDERRORLIB_VISIBLE /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
#else
# define ZSTDERRORLIB_API ZSTDERRORLIB_VISIBLE
#endif
/*-*********************************************
* Error codes list
*-*********************************************
* Error codes _values_ are pinned down since v1.3.1 only.
* Therefore, don't rely on values if you may link to any version < v1.3.1.
*
* Only values < 100 are considered stable.
*
* note 1 : this API shall be used with static linking only.
* dynamic linking is not yet officially supported.
* note 2 : Prefer relying on the enum than on its value whenever possible
* This is the only supported way to use the error list < v1.3.1
* note 3 : ZSTD_isError() is always correct, whatever the library version.
**********************************************/
typedef enum {
ZSTD_error_no_error = 0,
ZSTD_error_GENERIC = 1,
ZSTD_error_prefix_unknown = 10,
ZSTD_error_version_unsupported = 12,
ZSTD_error_frameParameter_unsupported = 14,
ZSTD_error_frameParameter_windowTooLarge = 16,
ZSTD_error_corruption_detected = 20,
ZSTD_error_checksum_wrong = 22,
ZSTD_error_literals_headerWrong = 24,
ZSTD_error_dictionary_corrupted = 30,
ZSTD_error_dictionary_wrong = 32,
ZSTD_error_dictionaryCreation_failed = 34,
ZSTD_error_parameter_unsupported = 40,
ZSTD_error_parameter_combination_unsupported = 41,
ZSTD_error_parameter_outOfBound = 42,
ZSTD_error_tableLog_tooLarge = 44,
ZSTD_error_maxSymbolValue_tooLarge = 46,
ZSTD_error_maxSymbolValue_tooSmall = 48,
ZSTD_error_cannotProduce_uncompressedBlock = 49,
ZSTD_error_stabilityCondition_notRespected = 50,
ZSTD_error_stage_wrong = 60,
ZSTD_error_init_missing = 62,
ZSTD_error_memory_allocation = 64,
ZSTD_error_workSpace_tooSmall= 66,
ZSTD_error_dstSize_tooSmall = 70,
ZSTD_error_srcSize_wrong = 72,
ZSTD_error_dstBuffer_null = 74,
ZSTD_error_noForwardProgress_destFull = 80,
ZSTD_error_noForwardProgress_inputEmpty = 82,
/* following error codes are __NOT STABLE__, they can be removed or changed in future versions */
ZSTD_error_frameIndex_tooLarge = 100,
ZSTD_error_seekableIO = 102,
ZSTD_error_dstBuffer_wrong = 104,
ZSTD_error_srcBuffer_wrong = 105,
ZSTD_error_sequenceProducer_failed = 106,
ZSTD_error_externalSequences_invalid = 107,
ZSTD_error_maxCode = 120 /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
} ZSTD_ErrorCode;
ZSTDERRORLIB_API const char* ZSTD_getErrorString(ZSTD_ErrorCode code); /**< Same as ZSTD_getErrorName, but using a `ZSTD_ErrorCode` enum argument */
#if defined (__cplusplus)
}
#endif
#endif /* ZSTD_ERRORS_H_398273423 */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+43
View File
@@ -0,0 +1,43 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
bitstream.h
Helper classes for reading/writing at the bit level.
***************************************************************************/
#pragma once
#ifndef __BITSTREAM_H__
#define __BITSTREAM_H__
#include <stdint.h>
/***************************************************************************
* TYPE DEFINITIONS
***************************************************************************
*/
/* helper class for reading from a bit buffer */
struct bitstream
{
uint32_t buffer; /* current bit accumulator */
int bits; /* number of bits in the accumulator */
const uint8_t * read; /* read pointer */
uint32_t doffset; /* byte offset within the data */
uint32_t dlength; /* length of the data */
};
struct bitstream* create_bitstream(const void *src, uint32_t srclength);
int bitstream_overflow(struct bitstream* bitstream);
uint32_t bitstream_read_offset(struct bitstream* bitstream);
uint32_t bitstream_read(struct bitstream* bitstream, int numbits);
uint32_t bitstream_peek(struct bitstream* bitstream, int numbits);
void bitstream_remove(struct bitstream* bitstream, int numbits);
uint32_t bitstream_flush(struct bitstream* bitstream);
#endif
+117
View File
@@ -0,0 +1,117 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
cdrom.h
Generic MAME cd-rom implementation
***************************************************************************/
#pragma once
#ifndef __CDROM_H__
#define __CDROM_H__
#include <stdint.h>
#include "chd.h"
#include "chdconfig.h"
#include "macros.h"
/***************************************************************************
CONSTANTS
***************************************************************************/
/* tracks are padded to a multiple of this many frames */
#define CD_TRACK_PADDING (4)
#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */
#define CD_MAX_SECTOR_DATA (2352)
#define CD_MAX_SUBCODE_DATA (96)
#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA)
#define CD_FRAMES_PER_HUNK (8)
#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6))
enum
{
CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */
CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */
CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */
CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */
CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */
CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */
CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */
CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */
CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */
};
enum
{
CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */
CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */
CD_SUB_NONE /* no subcode data stored */
};
#define CD_FLAG_GDROM 0x00000001 /* disc is a GD-ROM, all tracks should be stored with GD-ROM metadata */
#define CD_FLAG_GDROMLE 0x00000002 /* legacy GD-ROM, with little-endian CDDA data */
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
#if WANT_RAW_DATA_SECTOR
/* ECC utilities */
int ecc_verify(const uint8_t *sector);
void ecc_generate(uint8_t *sector);
void ecc_clear(uint8_t *sector);
#endif
chd_error cd_codec_decompress(
uint8_t *buffer,
void *base_decompressor, chd_codec_interface_decompress base_decompress,
void *subcode_decompressor, chd_codec_interface_decompress subcode_decompress,
const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
static CHDR_INLINE uint32_t msf_to_lba(uint32_t msf)
{
return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
}
static CHDR_INLINE uint32_t lba_to_msf(uint32_t lba)
{
uint8_t m, s, f;
m = lba / (60 * 75);
lba -= m * (60 * 75);
s = lba / 75;
f = lba % 75;
return ((m / 10) << 20) | ((m % 10) << 16) |
((s / 10) << 12) | ((s % 10) << 8) |
((f / 10) << 4) | ((f % 10) << 0);
}
/**
* segacd needs it like this.. investigate
* Angelo also says PCE tracks often start playing at the
* wrong address.. related?
**/
static CHDR_INLINE uint32_t lba_to_msf_alt(int lba)
{
uint32_t ret = 0;
ret |= ((lba / (60 * 75))&0xff)<<16;
ret |= (((lba / 75) % 60)&0xff)<<8;
ret |= ((lba % 75)&0xff)<<0;
return ret;
}
#endif /* __CDROM_H__ */
+430
View File
@@ -0,0 +1,430 @@
/***************************************************************************
chd.h
MAME Compressed Hunks of Data file format
****************************************************************************
Copyright Aaron Giles
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name 'MAME' nor the names of its contributors may be
used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
#pragma once
#ifndef __CHD_H__
#define __CHD_H__
#include "coretypes.h"
#ifdef __cplusplus
extern "C" {
#endif
/***************************************************************************
Compressed Hunks of Data header format. All numbers are stored in
Motorola (big-endian) byte ordering. The header is 76 (V1) or 80 (V2)
bytes long.
V1 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t hunksize; // 512-byte sectors per hunk
[ 28] uint32_t totalhunks; // total # of hunks represented
[ 32] uint32_t cylinders; // number of cylinders on hard disk
[ 36] uint32_t heads; // number of heads on hard disk
[ 40] uint32_t sectors; // number of sectors on hard disk
[ 44] uint8_t md5[16]; // MD5 checksum of raw data
[ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file
[ 76] (V1 header length)
V2 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t hunksize; // seclen-byte sectors per hunk
[ 28] uint32_t totalhunks; // total # of hunks represented
[ 32] uint32_t cylinders; // number of cylinders on hard disk
[ 36] uint32_t heads; // number of heads on hard disk
[ 40] uint32_t sectors; // number of sectors on hard disk
[ 44] uint8_t md5[16]; // MD5 checksum of raw data
[ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file
[ 76] uint32_t seclen; // number of bytes per sector
[ 80] (V2 header length)
V3 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t totalhunks; // total # of hunks represented
[ 28] uint64_t logicalbytes; // logical size of the data (in bytes)
[ 36] uint64_t metaoffset; // offset to the first blob of metadata
[ 44] uint8_t md5[16]; // MD5 checksum of raw data
[ 60] uint8_t parentmd5[16]; // MD5 checksum of parent file
[ 76] uint32_t hunkbytes; // number of bytes per hunk
[ 80] uint8_t sha1[20]; // SHA1 checksum of raw data
[100] uint8_t parentsha1[20];// SHA1 checksum of parent file
[120] (V3 header length)
V4 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t flags; // flags (see below)
[ 20] uint32_t compression; // compression type
[ 24] uint32_t totalhunks; // total # of hunks represented
[ 28] uint64_t logicalbytes; // logical size of the data (in bytes)
[ 36] uint64_t metaoffset; // offset to the first blob of metadata
[ 44] uint32_t hunkbytes; // number of bytes per hunk
[ 48] uint8_t sha1[20]; // combined raw+meta SHA1
[ 68] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent
[ 88] uint8_t rawsha1[20]; // raw data SHA1
[108] (V4 header length)
Flags:
0x00000001 - set if this drive has a parent
0x00000002 - set if this drive allows writes
=========================================================================
V5 header:
[ 0] char tag[8]; // 'MComprHD'
[ 8] uint32_t length; // length of header (including tag and length fields)
[ 12] uint32_t version; // drive format version
[ 16] uint32_t compressors[4];// which custom compressors are used?
[ 32] uint64_t logicalbytes; // logical size of the data (in bytes)
[ 40] uint64_t mapoffset; // offset to the map
[ 48] uint64_t metaoffset; // offset to the first blob of metadata
[ 56] uint32_t hunkbytes; // number of bytes per hunk (512k maximum)
[ 60] uint32_t unitbytes; // number of bytes per unit within each hunk
[ 64] uint8_t rawsha1[20]; // raw data SHA1
[ 84] uint8_t sha1[20]; // combined raw+meta SHA1
[104] uint8_t parentsha1[20];// combined raw+meta SHA1 of parent
[124] (V5 header length)
If parentsha1 != 0, we have a parent (no need for flags)
If compressors[0] == 0, we are uncompressed (including maps)
V5 uncompressed map format:
[ 0] uint32_t offset; // starting offset / hunk size
V5 compressed map format header:
[ 0] uint32_t length; // length of compressed map
[ 4] UINT48 datastart; // offset of first block
[ 10] uint16_t crc; // crc-16 of the map
[ 12] uint8_t lengthbits; // bits used to encode complength
[ 13] uint8_t hunkbits; // bits used to encode self-refs
[ 14] uint8_t parentunitbits; // bits used to encode parent unit refs
[ 15] uint8_t reserved; // future use
[ 16] (compressed header length)
Each compressed map entry, once expanded, looks like:
[ 0] uint8_t compression; // compression type
[ 1] UINT24 complength; // compressed length
[ 4] UINT48 offset; // offset
[ 10] uint16_t crc; // crc-16 of the data
***************************************************************************/
/***************************************************************************
CONSTANTS
***************************************************************************/
/* header information */
#define CHD_HEADER_VERSION 5
#define CHD_V1_HEADER_SIZE 76
#define CHD_V2_HEADER_SIZE 80
#define CHD_V3_HEADER_SIZE 120
#define CHD_V4_HEADER_SIZE 108
#define CHD_V5_HEADER_SIZE 124
#define CHD_MAX_HEADER_SIZE CHD_V5_HEADER_SIZE
/* checksumming information */
#define CHD_MD5_BYTES 16
#define CHD_SHA1_BYTES 20
/* CHD global flags */
#define CHDFLAGS_HAS_PARENT 0x00000001
#define CHDFLAGS_IS_WRITEABLE 0x00000002
#define CHDFLAGS_UNDEFINED 0xfffffffc
#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
/* compression types */
#define CHDCOMPRESSION_NONE 0
#define CHDCOMPRESSION_ZLIB 1
#define CHDCOMPRESSION_ZLIB_PLUS 2
#define CHDCOMPRESSION_AV 3
#define CHD_CODEC_NONE 0
#define CHD_CODEC_ZLIB CHD_MAKE_TAG('z','l','i','b')
#define CHD_CODEC_LZMA CHD_MAKE_TAG('l','z','m','a')
#define CHD_CODEC_HUFFMAN CHD_MAKE_TAG('h','u','f','f')
#define CHD_CODEC_FLAC CHD_MAKE_TAG('f','l','a','c')
#define CHD_CODEC_ZSTD CHD_MAKE_TAG('z', 's', 't', 'd')
/* general codecs with CD frontend */
#define CHD_CODEC_CD_ZLIB CHD_MAKE_TAG('c','d','z','l')
#define CHD_CODEC_CD_LZMA CHD_MAKE_TAG('c','d','l','z')
#define CHD_CODEC_CD_FLAC CHD_MAKE_TAG('c','d','f','l')
#define CHD_CODEC_CD_ZSTD CHD_MAKE_TAG('c','d','z','s')
/* A/V codec configuration parameters */
#define AV_CODEC_COMPRESS_CONFIG 1
#define AV_CODEC_DECOMPRESS_CONFIG 2
/* metadata parameters */
#define CHDMETATAG_WILDCARD 0
#define CHD_METAINDEX_APPEND ((uint32_t)-1)
/* metadata flags */
#define CHD_MDFLAGS_CHECKSUM 0x01 /* indicates data is checksummed */
/* standard hard disk metadata */
#define HARD_DISK_METADATA_TAG CHD_MAKE_TAG('G','D','D','D')
#define HARD_DISK_METADATA_FORMAT "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d"
/* hard disk identify information */
#define HARD_DISK_IDENT_METADATA_TAG CHD_MAKE_TAG('I','D','N','T')
/* hard disk key information */
#define HARD_DISK_KEY_METADATA_TAG CHD_MAKE_TAG('K','E','Y',' ')
/* pcmcia CIS information */
#define PCMCIA_CIS_METADATA_TAG CHD_MAKE_TAG('C','I','S',' ')
/* standard CD-ROM metadata */
#define CDROM_OLD_METADATA_TAG CHD_MAKE_TAG('C','H','C','D')
#define CDROM_TRACK_METADATA_TAG CHD_MAKE_TAG('C','H','T','R')
#define CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d"
#define CDROM_TRACK_METADATA2_TAG CHD_MAKE_TAG('C','H','T','2')
#define CDROM_TRACK_METADATA2_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d"
#define GDROM_OLD_METADATA_TAG CHD_MAKE_TAG('C','H','G','T')
#define GDROM_TRACK_METADATA_TAG CHD_MAKE_TAG('C', 'H', 'G', 'D')
#define GDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d"
/* standard A/V metadata */
#define AV_METADATA_TAG CHD_MAKE_TAG('A','V','A','V')
#define AV_METADATA_FORMAT "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d"
/* A/V laserdisc frame metadata */
#define AV_LD_METADATA_TAG CHD_MAKE_TAG('A','V','L','D')
/* DVD metadata */
#define DVD_METADATA_TAG CHD_MAKE_TAG('D','V','D',' ')
/* CHD open values */
#define CHD_OPEN_READ 1
#define CHD_OPEN_READWRITE 2
/* error types */
enum _chd_error
{
CHDERR_NONE,
CHDERR_NO_INTERFACE,
CHDERR_OUT_OF_MEMORY,
CHDERR_INVALID_FILE,
CHDERR_INVALID_PARAMETER,
CHDERR_INVALID_DATA,
CHDERR_FILE_NOT_FOUND,
CHDERR_REQUIRES_PARENT,
CHDERR_FILE_NOT_WRITEABLE,
CHDERR_READ_ERROR,
CHDERR_WRITE_ERROR,
CHDERR_CODEC_ERROR,
CHDERR_INVALID_PARENT,
CHDERR_HUNK_OUT_OF_RANGE,
CHDERR_DECOMPRESSION_ERROR,
CHDERR_COMPRESSION_ERROR,
CHDERR_CANT_CREATE_FILE,
CHDERR_CANT_VERIFY,
CHDERR_NOT_SUPPORTED,
CHDERR_METADATA_NOT_FOUND,
CHDERR_INVALID_METADATA_SIZE,
CHDERR_UNSUPPORTED_VERSION,
CHDERR_VERIFY_INCOMPLETE,
CHDERR_INVALID_METADATA,
CHDERR_INVALID_STATE,
CHDERR_OPERATION_PENDING,
CHDERR_NO_ASYNC_OPERATION,
CHDERR_UNSUPPORTED_FORMAT
};
typedef enum _chd_error chd_error;
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
/* opaque types */
typedef struct _chd_file chd_file;
/* extract header structure (NOT the on-disk header structure) */
typedef struct _chd_header chd_header;
struct _chd_header
{
uint32_t length; /* length of header data */
uint32_t version; /* drive format version */
uint32_t flags; /* flags field */
uint32_t compression[4]; /* compression type */
uint32_t hunkbytes; /* number of bytes per hunk */
uint32_t totalhunks; /* total # of hunks represented */
uint64_t logicalbytes; /* logical size of the data */
uint64_t metaoffset; /* offset in file of first metadata */
uint64_t mapoffset; /* TOOD V5 */
uint8_t md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
uint8_t parentmd5[CHD_MD5_BYTES]; /* overall MD5 checksum of parent */
uint8_t sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
uint8_t rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
uint8_t parentsha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum of parent */
uint32_t unitbytes; /* TODO V5 */
uint64_t unitcount; /* TODO V5 */
uint32_t hunkcount; /* TODO V5 */
/* map information */
uint32_t mapentrybytes; /* length of each entry in a map (V5) */
uint8_t* rawmap; /* raw map data */
uint32_t obsolete_cylinders; /* obsolete field -- do not use! */
uint32_t obsolete_sectors; /* obsolete field -- do not use! */
uint32_t obsolete_heads; /* obsolete field -- do not use! */
uint32_t obsolete_hunksize; /* obsolete field -- do not use! */
};
/* structure for returning information about a verification pass */
typedef struct _chd_verify_result chd_verify_result;
struct _chd_verify_result
{
uint8_t md5[CHD_MD5_BYTES]; /* overall MD5 checksum */
uint8_t sha1[CHD_SHA1_BYTES]; /* overall SHA1 checksum */
uint8_t rawsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */
uint8_t metasha1[CHD_SHA1_BYTES]; /* SHA1 checksum of metadata */
};
typedef chd_error (*chd_codec_interface_decompress)(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
/***************************************************************************
FUNCTION PROTOTYPES
***************************************************************************/
#ifdef _MSC_VER
#ifdef CHD_DLL
#ifdef CHD_DLL_EXPORTS
#define CHD_EXPORT __declspec(dllexport)
#else
#define CHD_EXPORT __declspec(dllimport)
#endif
#else
#define CHD_EXPORT
#endif
#else
#define CHD_EXPORT __attribute__ ((visibility("default")))
#endif
/* ----- CHD file management ----- */
/* create a new CHD file fitting the given description */
/* chd_error chd_create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t compression, chd_file *parent); */
/* same as chd_create(), but accepts an already-opened core_file object */
/* chd_error chd_create_file(core_file *file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t compression, chd_file *parent); */
/* open an existing CHD file */
CHD_EXPORT chd_error chd_open_core_file_callbacks(const core_file_callbacks *callbacks, const void *user_data, int mode, chd_file *parent, chd_file **chd);
CHD_EXPORT chd_error chd_open_core_file(core_file *file, int mode, chd_file *parent, chd_file **chd); /* Legacy; use chd_open_core_file_callbacks instead! */
CHD_EXPORT chd_error chd_open_file(FILE *file, int mode, chd_file *parent, chd_file **chd);
CHD_EXPORT chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd);
/* precache underlying file */
CHD_EXPORT chd_error chd_precache(chd_file *chd);
/* close a CHD file */
CHD_EXPORT void chd_close(chd_file *chd);
/* return the associated core_file */
CHD_EXPORT core_file *chd_core_file(chd_file *chd);
/* return an error string for the given CHD error */
CHD_EXPORT const char *chd_error_string(chd_error err);
/* ----- CHD header management ----- */
/* return a pointer to the extracted CHD header data */
CHD_EXPORT const chd_header *chd_get_header(chd_file *chd);
/* read CHD header data from file into the pointed struct */
CHD_EXPORT chd_error chd_read_header_core_file_callbacks(const core_file_callbacks *callback, const void *user_data, chd_header *header);
CHD_EXPORT chd_error chd_read_header_core_file(core_file *file, chd_header *header); /* Legacy; use chd_read_header_core_file_callbacks instead! */
CHD_EXPORT chd_error chd_read_header_file(FILE *file, chd_header *header);
CHD_EXPORT chd_error chd_read_header(const char *filename, chd_header *header);
/* ----- core data read/write ----- */
/* read one hunk from the CHD file */
CHD_EXPORT chd_error chd_read(chd_file *chd, uint32_t hunknum, void *buffer);
/* ----- metadata management ----- */
/* get indexed metadata of a particular sort */
CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, uint32_t searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t *resultlen, uint32_t *resulttag, uint8_t *resultflags);
#ifdef __cplusplus
}
#endif
#endif /* __CHD_H__ */
+18
View File
@@ -0,0 +1,18 @@
#ifndef __CHDCONFIG_H__
#define __CHDCONFIG_H__
/* Configure CHDR features by defining these beforehand. */
#ifndef WANT_RAW_DATA_SECTOR
#define WANT_RAW_DATA_SECTOR 1
#endif
#ifndef WANT_SUBCODE
#define WANT_SUBCODE 1
#endif
#ifndef VERIFY_BLOCK_CRC
#define VERIFY_BLOCK_CRC 1
#endif
#endif
+75
View File
@@ -0,0 +1,75 @@
#ifndef __CORETYPES_H__
#define __CORETYPES_H__
#include <stdint.h>
#include <stdio.h>
#ifdef USE_LIBRETRO_VFS
#include <streams/file_stream_transforms.h>
#endif
#include "macros.h"
typedef struct chd_core_file_callbacks {
/*
* return the size of a given file as a 64-bit unsigned integer.
* the position of the file pointer after calling this function is
* undefined because many implementations will seek to the end of the
* file and call ftell.
*
* on error, (uint64_t)-1 is returned.
*/
uint64_t(*fsize)(void*);
/*
* should match the behavior of fread, except the FILE* argument at the end
* will be replaced with a void*.
*/
size_t(*fread)(void*,size_t,size_t,void*);
// closes the given file.
int (*fclose)(void*);
// fseek clone
int (*fseek)(void*, int64_t, int);
} core_file_callbacks;
typedef struct chd_core_file_callbacks_and_argp {
const core_file_callbacks *callbacks;
/*
* arbitrary pointer to data the implementation uses to implement the above functions
*/
void *argp;
} core_file_callbacks_and_argp;
/* Legacy API */
typedef struct chd_core_file {
void *argp;
uint64_t(*fsize)(struct chd_core_file*);
size_t(*fread)(void*,size_t,size_t,struct chd_core_file*);
int (*fclose)(struct chd_core_file*);
int (*fseek)(struct chd_core_file*, int64_t, int);
} core_file;
/* File IO shortcuts */
static CHDR_INLINE int core_fclose(const core_file_callbacks_and_argp *fp) {
return fp->callbacks->fclose(fp->argp);
}
static CHDR_INLINE size_t core_fread(const core_file_callbacks_and_argp *fp, void *ptr, size_t len) {
return fp->callbacks->fread(ptr, 1, len, fp->argp);
}
static CHDR_INLINE int core_fseek(const core_file_callbacks_and_argp* fp, int64_t offset, int whence) {
return fp->callbacks->fseek(fp->argp, offset, whence);
}
static CHDR_INLINE uint64_t core_fsize(const core_file_callbacks_and_argp *fp)
{
return fp->callbacks->fsize(fp->argp);
}
#endif
+51
View File
@@ -0,0 +1,51 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
flac.h
FLAC compression wrappers
***************************************************************************/
#pragma once
#ifndef __FLAC_H__
#define __FLAC_H__
#include <stdint.h>
/***************************************************************************
* TYPE DEFINITIONS
***************************************************************************
*/
typedef struct _flac_decoder flac_decoder;
struct _flac_decoder {
/* output state */
void * decoder; /* actual encoder */
uint32_t sample_rate; /* decoded sample rate */
uint8_t channels; /* decoded number of channels */
uint8_t bits_per_sample; /* decoded bits per sample */
uint32_t compressed_offset; /* current offset in compressed data */
const uint8_t * compressed_start; /* start of compressed data */
uint32_t compressed_length; /* length of compressed data */
const uint8_t * compressed2_start; /* start of compressed data */
uint32_t compressed2_length; /* length of compressed data */
int16_t * uncompressed_start[8]; /* pointer to start of uncompressed data (up to 8 streams) */
uint32_t uncompressed_offset; /* current position in uncompressed data */
uint32_t uncompressed_length; /* length of uncompressed data */
int uncompressed_swap; /* swap uncompressed sample data */
uint8_t custom_header[0x2a]; /* custom header */
};
/* ======================> flac_decoder */
int flac_decoder_init(flac_decoder* decoder);
void flac_decoder_free(flac_decoder* decoder);
int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length);
int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian);
uint32_t flac_decoder_finish(flac_decoder* decoder);
int flac_decoder_detect_native_endian(void);
#endif /* __FLAC_H__ */
+90
View File
@@ -0,0 +1,90 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
huffman.h
Static Huffman compression and decompression helpers.
***************************************************************************/
#pragma once
#ifndef __HUFFMAN_H__
#define __HUFFMAN_H__
#include "bitstream.h"
/***************************************************************************
* CONSTANTS
***************************************************************************
*/
enum huffman_error
{
HUFFERR_NONE = 0,
HUFFERR_TOO_MANY_BITS,
HUFFERR_INVALID_DATA,
HUFFERR_INPUT_BUFFER_TOO_SMALL,
HUFFERR_OUTPUT_BUFFER_TOO_SMALL,
HUFFERR_INTERNAL_INCONSISTENCY,
HUFFERR_TOO_MANY_CONTEXTS
};
/***************************************************************************
* TYPE DEFINITIONS
***************************************************************************
*/
typedef uint16_t lookup_value;
/* a node in the huffman tree */
struct node_t
{
struct node_t* parent; /* pointer to parent node */
uint32_t count; /* number of hits on this node */
uint32_t weight; /* assigned weight of this node */
uint32_t bits; /* bits used to encode the node */
uint8_t numbits; /* number of bits needed for this node */
};
/* ======================> huffman_context_base */
/* context class for decoding */
struct huffman_decoder
{
/* internal state */
uint32_t numcodes; /* number of total codes being processed */
uint8_t maxbits; /* maximum bits per code */
uint8_t prevdata; /* value of the previous data (for delta-RLE encoding) */
int rleremaining; /* number of RLE bytes remaining (for delta-RLE encoding) */
lookup_value * lookup; /* pointer to the lookup table */
struct node_t * huffnode; /* array of nodes */
uint32_t * datahisto; /* histogram of data values */
/* array versions of the info we need */
#if 0
node_t* huffnode_array; /* [_NumCodes]; */
lookup_value* lookup_array; /* [1 << _MaxBits]; */
#endif
};
/* ======================> huffman_decoder */
struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits);
void delete_huffman_decoder(struct huffman_decoder* decoder);
/* single item operations */
uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf);
enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf);
enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf);
int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight);
enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder);
enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder);
enum huffman_error huffman_build_lookup_table(struct huffman_decoder* decoder);
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef LIBCHDR_MACROS_H
#define LIBCHDR_MACROS_H
#undef ARRAY_LENGTH
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
#undef MAX
#undef MIN
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#ifndef CHDR_INLINE
#if defined(_WIN32) || defined(__INTEL_COMPILER)
#define CHDR_INLINE __inline
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define CHDR_INLINE inline
#elif defined(__GNUC__)
#define CHDR_INLINE __inline__
#else
#define CHDR_INLINE
#endif
#endif
#endif /* LIBCHDR_MACROS_H */
+28
View File
@@ -0,0 +1,28 @@
#ifndef LIBCHDR_CODEC_CDFL_H
#define LIBCHDR_CODEC_CDFL_H
#include <stdint.h>
#include "../include/libchdr/chd.h"
#include "../include/libchdr/chdconfig.h"
#include "../include/libchdr/flac.h"
#include "codec_zlib.h"
/* codec-private data for the CDFL codec */
typedef struct _cdfl_codec_data cdfl_codec_data;
struct _cdfl_codec_data {
/* internal state */
int swap_endian;
flac_decoder decoder;
#if WANT_SUBCODE
zlib_codec_data subcode_decompressor;
#endif
uint8_t* buffer;
};
/* cdfl compression codec */
chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes);
void cdfl_codec_free(void* codec);
chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_CDFL_H */
+27
View File
@@ -0,0 +1,27 @@
#ifndef LIBCHDR_CODEC_CDLZ_H
#define LIBCHDR_CODEC_CDLZ_H
#include <stdint.h>
#include "../include/libchdr/chd.h"
#include "../include/libchdr/chdconfig.h"
#include "codec_lzma.h"
#include "codec_zlib.h"
/* codec-private data for the CDLZ codec */
typedef struct _cdlz_codec_data cdlz_codec_data;
struct _cdlz_codec_data {
/* internal state */
lzma_codec_data base_decompressor;
#if WANT_SUBCODE
zlib_codec_data subcode_decompressor;
#endif
uint8_t* buffer;
};
/* cdlz compression codec */
chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes);
void cdlz_codec_free(void* codec);
chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_CDLZ_H */
+26
View File
@@ -0,0 +1,26 @@
#ifndef LIBCHDR_CODEC_CDZL_H
#define LIBCHDR_CODEC_CDZL_H
#include <stdint.h>
#include "../include/libchdr/chd.h"
#include "../include/libchdr/chdconfig.h"
#include "codec_zlib.h"
/* codec-private data for the CDZL codec */
typedef struct _cdzl_codec_data cdzl_codec_data;
struct _cdzl_codec_data {
/* internal state */
zlib_codec_data base_decompressor;
#if WANT_SUBCODE
zlib_codec_data subcode_decompressor;
#endif
uint8_t* buffer;
};
/* cdzl compression codec */
chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes);
void cdzl_codec_free(void* codec);
chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_CDZL_H */
+26
View File
@@ -0,0 +1,26 @@
#ifndef LIBCHDR_CODEC_CDZS_H
#define LIBCHDR_CODEC_CDZS_H
#include <stdint.h>
#include "../include/libchdr/chd.h"
#include "../include/libchdr/chdconfig.h"
#include "codec_zstd.h"
/* codec-private data for the CDZS codec */
typedef struct _cdzs_codec_data cdzs_codec_data;
struct _cdzs_codec_data
{
zstd_codec_data base_decompressor;
#if WANT_SUBCODE
zstd_codec_data subcode_decompressor;
#endif
uint8_t* buffer;
};
/* cdlz compression codec */
chd_error cdzs_codec_init(void *codec, uint32_t hunkbytes);
void cdzs_codec_free(void *codec);
chd_error cdzs_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_CDZS_H */
+22
View File
@@ -0,0 +1,22 @@
#ifndef LIBCHDR_CODEC_FLAC_H
#define LIBCHDR_CODEC_FLAC_H
#include <stdint.h>
#include "../include/libchdr/chd.h"
#include "../include/libchdr/flac.h"
/* codec-private data for the FLAC codec */
typedef struct _flac_codec_data flac_codec_data;
struct _flac_codec_data {
/* internal state */
int native_endian;
flac_decoder decoder;
};
/* flac compression codec */
chd_error flac_codec_init(void *codec, uint32_t hunkbytes);
void flac_codec_free(void *codec);
chd_error flac_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_FLAC_H */
+22
View File
@@ -0,0 +1,22 @@
#ifndef LIBCHDR_CODEC_HUFF_H
#define LIBCHDR_CODEC_HUFF_H
#include <stdint.h>
#include "../include/libchdr/chd.h"
struct huffman_decoder;
/* codec-private data for the FLAC codec */
typedef struct _huff_codec_data huff_codec_data;
struct _huff_codec_data
{
struct huffman_decoder* decoder;
};
/* huff compression codec */
chd_error huff_codec_init(void *codec, uint32_t hunkbytes);
void huff_codec_free(void *codec);
chd_error huff_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_HUFF_H */
+33
View File
@@ -0,0 +1,33 @@
#ifndef LIBCHDR_CODEC_LZMA_H
#define LIBCHDR_CODEC_LZMA_H
#include <stdint.h>
#include "../deps/lzma-25.01/include/LzmaDec.h"
#include "../include/libchdr/chd.h"
/* codec-private data for the LZMA codec */
#define MAX_LZMA_ALLOCS 64
typedef struct _lzma_allocator lzma_allocator;
struct _lzma_allocator
{
ISzAlloc base; /* must be first member for (ISzAlloc*) cast compatibility */
uint32_t* allocptr[MAX_LZMA_ALLOCS];
uint32_t* allocptr2[MAX_LZMA_ALLOCS];
};
typedef struct _lzma_codec_data lzma_codec_data;
struct _lzma_codec_data
{
CLzmaDec decoder;
lzma_allocator allocator;
};
/* lzma compression codec */
chd_error lzma_codec_init(void *codec, uint32_t hunkbytes);
void lzma_codec_free(void *codec);
chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_LZMA_H */
+41
View File
@@ -0,0 +1,41 @@
#ifndef LIBCHDR_CODEC_ZLIB_H
#define LIBCHDR_CODEC_ZLIB_H
#include <stdint.h>
#if defined(__PS3__) || defined(__PSL1GHT__)
#define __MACTYPES__
#endif
#ifdef CHDR_SYSTEM_ZLIB
#include <zlib.h>
typedef uInt zlib_alloc_size;
#else
#include "../deps/miniz-3.1.1/miniz.h"
typedef size_t zlib_alloc_size;
#endif
#include "../include/libchdr/chd.h"
/* codec-private data for the ZLIB codec */
#define MAX_ZLIB_ALLOCS 64
typedef struct _zlib_allocator zlib_allocator;
struct _zlib_allocator
{
uint32_t * allocptr[MAX_ZLIB_ALLOCS];
uint32_t * allocptr2[MAX_ZLIB_ALLOCS];
};
typedef struct _zlib_codec_data zlib_codec_data;
struct _zlib_codec_data
{
z_stream inflater;
zlib_allocator allocator;
};
/* zlib compression codec */
chd_error zlib_codec_init(void *codec, uint32_t hunkbytes);
void zlib_codec_free(void *codec);
chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_ZLIB_H */
+27
View File
@@ -0,0 +1,27 @@
#ifndef LIBCHDR_CODEC_ZSTD_H
#define LIBCHDR_CODEC_ZSTD_H
#include <stdint.h>
#ifdef CHDR_SYSTEM_ZSTD
#include <zstd.h>
#else
#include "../deps/zstd-1.5.7/zstd.h"
#endif
#include "../include/libchdr/chd.h"
/* codec-private data for the ZSTD codec */
typedef struct _zstd_codec_data zstd_codec_data;
struct _zstd_codec_data
{
ZSTD_DStream *dstream;
};
/* zstd compression codec */
chd_error zstd_codec_init(void *codec, uint32_t hunkbytes);
void zstd_codec_free(void *codec);
chd_error zstd_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
#endif /* LIBCHDR_CODEC_ZSTD_H */
+125
View File
@@ -0,0 +1,125 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
bitstream.c
Helper classes for reading/writing at the bit level.
***************************************************************************/
#include <stdlib.h>
#include "../include/libchdr/bitstream.h"
/***************************************************************************
* INLINE FUNCTIONS
***************************************************************************
*/
int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); }
/*-------------------------------------------------
* create_bitstream - constructor
*-------------------------------------------------
*/
struct bitstream* create_bitstream(const void *src, uint32_t srclength)
{
struct bitstream* bitstream = (struct bitstream*)malloc(sizeof(struct bitstream));
bitstream->buffer = 0;
bitstream->bits = 0;
bitstream->read = (const uint8_t*)src;
bitstream->doffset = 0;
bitstream->dlength = srclength;
return bitstream;
}
/*-----------------------------------------------------
* bitstream_peek - fetch the requested number of bits
* but don't advance the input pointer
*-----------------------------------------------------
*/
uint32_t bitstream_peek(struct bitstream* bitstream, int numbits)
{
if (numbits == 0)
return 0;
/* fetch data if we need more */
if (numbits > bitstream->bits)
{
while (bitstream->bits <= 24)
{
if (bitstream->doffset < bitstream->dlength)
bitstream->buffer |= bitstream->read[bitstream->doffset] << (24 - bitstream->bits);
bitstream->doffset++;
bitstream->bits += 8;
}
}
/* return the data */
return bitstream->buffer >> (32 - numbits);
}
/*-----------------------------------------------------
* bitstream_remove - advance the input pointer by the
* specified number of bits
*-----------------------------------------------------
*/
void bitstream_remove(struct bitstream* bitstream, int numbits)
{
bitstream->buffer <<= numbits;
bitstream->bits -= numbits;
}
/*-----------------------------------------------------
* bitstream_read - fetch the requested number of bits
*-----------------------------------------------------
*/
uint32_t bitstream_read(struct bitstream* bitstream, int numbits)
{
uint32_t result = bitstream_peek(bitstream, numbits);
bitstream_remove(bitstream, numbits);
return result;
}
/*-------------------------------------------------
* read_offset - return the current read offset
*-------------------------------------------------
*/
uint32_t bitstream_read_offset(struct bitstream* bitstream)
{
uint32_t result = bitstream->doffset;
int bits = bitstream->bits;
while (bits >= 8)
{
result--;
bits -= 8;
}
return result;
}
/*-------------------------------------------------
* flush - flush to the nearest byte
*-------------------------------------------------
*/
uint32_t bitstream_flush(struct bitstream* bitstream)
{
while (bitstream->bits >= 8)
{
bitstream->doffset--;
bitstream->bits -= 8;
}
bitstream->bits = bitstream->buffer = 0;
return bitstream->doffset;
}
+492
View File
@@ -0,0 +1,492 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
cdrom.c
Generic MAME CD-ROM utilities - build IDE and SCSI CD-ROMs on top of this
****************************************************************************
IMPORTANT:
"physical" block addresses are the actual addresses on the emulated CD.
"chd" block addresses are the block addresses in the CHD file.
Because we pad each track to a 4-frame boundary, these addressing
schemes will differ after track 1!
***************************************************************************/
#include <string.h>
#include "../include/libchdr/cdrom.h"
#if WANT_RAW_DATA_SECTOR
/***************************************************************************
DEBUGGING
***************************************************************************/
/** @brief The verbose. */
#define VERBOSE (0)
#if VERBOSE
/**
* @def LOG(x) do
*
* @brief A macro that defines log.
*
* @param x The void to process.
*/
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
/**
* @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
*
* @brief Logerrors the given text.
*
* @param text The text.
*
* @return A CLIB_DECL.
*/
void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
#else
/**
* @def LOG(x);
*
* @brief A macro that defines log.
*
* @param x The void to process.
*/
#define LOG(x)
#endif
/***************************************************************************
CONSTANTS
***************************************************************************/
/** @brief offset within sector. */
#define SYNC_OFFSET 0x000
/** @brief 12 bytes. */
#define SYNC_NUM_BYTES 12
/** @brief offset within sector. */
#define MODE_OFFSET 0x00f
/** @brief offset within sector. */
#define ECC_P_OFFSET 0x81c
/** @brief 2 lots of 86. */
#define ECC_P_NUM_BYTES 86
/** @brief 24 bytes each. */
#define ECC_P_COMP 24
/** @brief The ECC q offset. */
#define ECC_Q_OFFSET (ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES)
/** @brief 2 lots of 52. */
#define ECC_Q_NUM_BYTES 52
/** @brief 43 bytes each. */
#define ECC_Q_COMP 43
#if WANT_RAW_DATA_SECTOR
static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 };
#endif
/**
* @brief -------------------------------------------------
* ECC lookup tables pre-calculated tables for ECC data calcs
* -------------------------------------------------.
*/
static const uint8_t ecclow[256] =
{
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
0x1d, 0x1f, 0x19, 0x1b, 0x15, 0x17, 0x11, 0x13, 0x0d, 0x0f, 0x09, 0x0b, 0x05, 0x07, 0x01, 0x03,
0x3d, 0x3f, 0x39, 0x3b, 0x35, 0x37, 0x31, 0x33, 0x2d, 0x2f, 0x29, 0x2b, 0x25, 0x27, 0x21, 0x23,
0x5d, 0x5f, 0x59, 0x5b, 0x55, 0x57, 0x51, 0x53, 0x4d, 0x4f, 0x49, 0x4b, 0x45, 0x47, 0x41, 0x43,
0x7d, 0x7f, 0x79, 0x7b, 0x75, 0x77, 0x71, 0x73, 0x6d, 0x6f, 0x69, 0x6b, 0x65, 0x67, 0x61, 0x63,
0x9d, 0x9f, 0x99, 0x9b, 0x95, 0x97, 0x91, 0x93, 0x8d, 0x8f, 0x89, 0x8b, 0x85, 0x87, 0x81, 0x83,
0xbd, 0xbf, 0xb9, 0xbb, 0xb5, 0xb7, 0xb1, 0xb3, 0xad, 0xaf, 0xa9, 0xab, 0xa5, 0xa7, 0xa1, 0xa3,
0xdd, 0xdf, 0xd9, 0xdb, 0xd5, 0xd7, 0xd1, 0xd3, 0xcd, 0xcf, 0xc9, 0xcb, 0xc5, 0xc7, 0xc1, 0xc3,
0xfd, 0xff, 0xf9, 0xfb, 0xf5, 0xf7, 0xf1, 0xf3, 0xed, 0xef, 0xe9, 0xeb, 0xe5, 0xe7, 0xe1, 0xe3
};
/** @brief The ecchigh[ 256]. */
static const uint8_t ecchigh[256] =
{
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
0xeb, 0x1f, 0x1e, 0xea, 0x1c, 0xe8, 0xe9, 0x1d, 0x18, 0xec, 0xed, 0x19, 0xef, 0x1b, 0x1a, 0xee,
0x10, 0xe4, 0xe5, 0x11, 0xe7, 0x13, 0x12, 0xe6, 0xe3, 0x17, 0x16, 0xe2, 0x14, 0xe0, 0xe1, 0x15,
0xcb, 0x3f, 0x3e, 0xca, 0x3c, 0xc8, 0xc9, 0x3d, 0x38, 0xcc, 0xcd, 0x39, 0xcf, 0x3b, 0x3a, 0xce,
0x30, 0xc4, 0xc5, 0x31, 0xc7, 0x33, 0x32, 0xc6, 0xc3, 0x37, 0x36, 0xc2, 0x34, 0xc0, 0xc1, 0x35,
0x20, 0xd4, 0xd5, 0x21, 0xd7, 0x23, 0x22, 0xd6, 0xd3, 0x27, 0x26, 0xd2, 0x24, 0xd0, 0xd1, 0x25,
0xdb, 0x2f, 0x2e, 0xda, 0x2c, 0xd8, 0xd9, 0x2d, 0x28, 0xdc, 0xdd, 0x29, 0xdf, 0x2b, 0x2a, 0xde,
0x8b, 0x7f, 0x7e, 0x8a, 0x7c, 0x88, 0x89, 0x7d, 0x78, 0x8c, 0x8d, 0x79, 0x8f, 0x7b, 0x7a, 0x8e,
0x70, 0x84, 0x85, 0x71, 0x87, 0x73, 0x72, 0x86, 0x83, 0x77, 0x76, 0x82, 0x74, 0x80, 0x81, 0x75,
0x60, 0x94, 0x95, 0x61, 0x97, 0x63, 0x62, 0x96, 0x93, 0x67, 0x66, 0x92, 0x64, 0x90, 0x91, 0x65,
0x9b, 0x6f, 0x6e, 0x9a, 0x6c, 0x98, 0x99, 0x6d, 0x68, 0x9c, 0x9d, 0x69, 0x9f, 0x6b, 0x6a, 0x9e,
0x40, 0xb4, 0xb5, 0x41, 0xb7, 0x43, 0x42, 0xb6, 0xb3, 0x47, 0x46, 0xb2, 0x44, 0xb0, 0xb1, 0x45,
0xbb, 0x4f, 0x4e, 0xba, 0x4c, 0xb8, 0xb9, 0x4d, 0x48, 0xbc, 0xbd, 0x49, 0xbf, 0x4b, 0x4a, 0xbe,
0xab, 0x5f, 0x5e, 0xaa, 0x5c, 0xa8, 0xa9, 0x5d, 0x58, 0xac, 0xad, 0x59, 0xaf, 0x5b, 0x5a, 0xae,
0x50, 0xa4, 0xa5, 0x51, 0xa7, 0x53, 0x52, 0xa6, 0xa3, 0x57, 0x56, 0xa2, 0x54, 0xa0, 0xa1, 0x55
};
/**
* @brief -------------------------------------------------
* poffsets - each row represents the addresses used to calculate a byte of the ECC P
* data 86 (*2) ECC P bytes, 24 values represented by each
* -------------------------------------------------.
*/
static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
{
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
{ 0x002,0x058,0x0ae,0x104,0x15a,0x1b0,0x206,0x25c,0x2b2,0x308,0x35e,0x3b4,0x40a,0x460,0x4b6,0x50c,0x562,0x5b8,0x60e,0x664,0x6ba,0x710,0x766,0x7bc },
{ 0x003,0x059,0x0af,0x105,0x15b,0x1b1,0x207,0x25d,0x2b3,0x309,0x35f,0x3b5,0x40b,0x461,0x4b7,0x50d,0x563,0x5b9,0x60f,0x665,0x6bb,0x711,0x767,0x7bd },
{ 0x004,0x05a,0x0b0,0x106,0x15c,0x1b2,0x208,0x25e,0x2b4,0x30a,0x360,0x3b6,0x40c,0x462,0x4b8,0x50e,0x564,0x5ba,0x610,0x666,0x6bc,0x712,0x768,0x7be },
{ 0x005,0x05b,0x0b1,0x107,0x15d,0x1b3,0x209,0x25f,0x2b5,0x30b,0x361,0x3b7,0x40d,0x463,0x4b9,0x50f,0x565,0x5bb,0x611,0x667,0x6bd,0x713,0x769,0x7bf },
{ 0x006,0x05c,0x0b2,0x108,0x15e,0x1b4,0x20a,0x260,0x2b6,0x30c,0x362,0x3b8,0x40e,0x464,0x4ba,0x510,0x566,0x5bc,0x612,0x668,0x6be,0x714,0x76a,0x7c0 },
{ 0x007,0x05d,0x0b3,0x109,0x15f,0x1b5,0x20b,0x261,0x2b7,0x30d,0x363,0x3b9,0x40f,0x465,0x4bb,0x511,0x567,0x5bd,0x613,0x669,0x6bf,0x715,0x76b,0x7c1 },
{ 0x008,0x05e,0x0b4,0x10a,0x160,0x1b6,0x20c,0x262,0x2b8,0x30e,0x364,0x3ba,0x410,0x466,0x4bc,0x512,0x568,0x5be,0x614,0x66a,0x6c0,0x716,0x76c,0x7c2 },
{ 0x009,0x05f,0x0b5,0x10b,0x161,0x1b7,0x20d,0x263,0x2b9,0x30f,0x365,0x3bb,0x411,0x467,0x4bd,0x513,0x569,0x5bf,0x615,0x66b,0x6c1,0x717,0x76d,0x7c3 },
{ 0x00a,0x060,0x0b6,0x10c,0x162,0x1b8,0x20e,0x264,0x2ba,0x310,0x366,0x3bc,0x412,0x468,0x4be,0x514,0x56a,0x5c0,0x616,0x66c,0x6c2,0x718,0x76e,0x7c4 },
{ 0x00b,0x061,0x0b7,0x10d,0x163,0x1b9,0x20f,0x265,0x2bb,0x311,0x367,0x3bd,0x413,0x469,0x4bf,0x515,0x56b,0x5c1,0x617,0x66d,0x6c3,0x719,0x76f,0x7c5 },
{ 0x00c,0x062,0x0b8,0x10e,0x164,0x1ba,0x210,0x266,0x2bc,0x312,0x368,0x3be,0x414,0x46a,0x4c0,0x516,0x56c,0x5c2,0x618,0x66e,0x6c4,0x71a,0x770,0x7c6 },
{ 0x00d,0x063,0x0b9,0x10f,0x165,0x1bb,0x211,0x267,0x2bd,0x313,0x369,0x3bf,0x415,0x46b,0x4c1,0x517,0x56d,0x5c3,0x619,0x66f,0x6c5,0x71b,0x771,0x7c7 },
{ 0x00e,0x064,0x0ba,0x110,0x166,0x1bc,0x212,0x268,0x2be,0x314,0x36a,0x3c0,0x416,0x46c,0x4c2,0x518,0x56e,0x5c4,0x61a,0x670,0x6c6,0x71c,0x772,0x7c8 },
{ 0x00f,0x065,0x0bb,0x111,0x167,0x1bd,0x213,0x269,0x2bf,0x315,0x36b,0x3c1,0x417,0x46d,0x4c3,0x519,0x56f,0x5c5,0x61b,0x671,0x6c7,0x71d,0x773,0x7c9 },
{ 0x010,0x066,0x0bc,0x112,0x168,0x1be,0x214,0x26a,0x2c0,0x316,0x36c,0x3c2,0x418,0x46e,0x4c4,0x51a,0x570,0x5c6,0x61c,0x672,0x6c8,0x71e,0x774,0x7ca },
{ 0x011,0x067,0x0bd,0x113,0x169,0x1bf,0x215,0x26b,0x2c1,0x317,0x36d,0x3c3,0x419,0x46f,0x4c5,0x51b,0x571,0x5c7,0x61d,0x673,0x6c9,0x71f,0x775,0x7cb },
{ 0x012,0x068,0x0be,0x114,0x16a,0x1c0,0x216,0x26c,0x2c2,0x318,0x36e,0x3c4,0x41a,0x470,0x4c6,0x51c,0x572,0x5c8,0x61e,0x674,0x6ca,0x720,0x776,0x7cc },
{ 0x013,0x069,0x0bf,0x115,0x16b,0x1c1,0x217,0x26d,0x2c3,0x319,0x36f,0x3c5,0x41b,0x471,0x4c7,0x51d,0x573,0x5c9,0x61f,0x675,0x6cb,0x721,0x777,0x7cd },
{ 0x014,0x06a,0x0c0,0x116,0x16c,0x1c2,0x218,0x26e,0x2c4,0x31a,0x370,0x3c6,0x41c,0x472,0x4c8,0x51e,0x574,0x5ca,0x620,0x676,0x6cc,0x722,0x778,0x7ce },
{ 0x015,0x06b,0x0c1,0x117,0x16d,0x1c3,0x219,0x26f,0x2c5,0x31b,0x371,0x3c7,0x41d,0x473,0x4c9,0x51f,0x575,0x5cb,0x621,0x677,0x6cd,0x723,0x779,0x7cf },
{ 0x016,0x06c,0x0c2,0x118,0x16e,0x1c4,0x21a,0x270,0x2c6,0x31c,0x372,0x3c8,0x41e,0x474,0x4ca,0x520,0x576,0x5cc,0x622,0x678,0x6ce,0x724,0x77a,0x7d0 },
{ 0x017,0x06d,0x0c3,0x119,0x16f,0x1c5,0x21b,0x271,0x2c7,0x31d,0x373,0x3c9,0x41f,0x475,0x4cb,0x521,0x577,0x5cd,0x623,0x679,0x6cf,0x725,0x77b,0x7d1 },
{ 0x018,0x06e,0x0c4,0x11a,0x170,0x1c6,0x21c,0x272,0x2c8,0x31e,0x374,0x3ca,0x420,0x476,0x4cc,0x522,0x578,0x5ce,0x624,0x67a,0x6d0,0x726,0x77c,0x7d2 },
{ 0x019,0x06f,0x0c5,0x11b,0x171,0x1c7,0x21d,0x273,0x2c9,0x31f,0x375,0x3cb,0x421,0x477,0x4cd,0x523,0x579,0x5cf,0x625,0x67b,0x6d1,0x727,0x77d,0x7d3 },
{ 0x01a,0x070,0x0c6,0x11c,0x172,0x1c8,0x21e,0x274,0x2ca,0x320,0x376,0x3cc,0x422,0x478,0x4ce,0x524,0x57a,0x5d0,0x626,0x67c,0x6d2,0x728,0x77e,0x7d4 },
{ 0x01b,0x071,0x0c7,0x11d,0x173,0x1c9,0x21f,0x275,0x2cb,0x321,0x377,0x3cd,0x423,0x479,0x4cf,0x525,0x57b,0x5d1,0x627,0x67d,0x6d3,0x729,0x77f,0x7d5 },
{ 0x01c,0x072,0x0c8,0x11e,0x174,0x1ca,0x220,0x276,0x2cc,0x322,0x378,0x3ce,0x424,0x47a,0x4d0,0x526,0x57c,0x5d2,0x628,0x67e,0x6d4,0x72a,0x780,0x7d6 },
{ 0x01d,0x073,0x0c9,0x11f,0x175,0x1cb,0x221,0x277,0x2cd,0x323,0x379,0x3cf,0x425,0x47b,0x4d1,0x527,0x57d,0x5d3,0x629,0x67f,0x6d5,0x72b,0x781,0x7d7 },
{ 0x01e,0x074,0x0ca,0x120,0x176,0x1cc,0x222,0x278,0x2ce,0x324,0x37a,0x3d0,0x426,0x47c,0x4d2,0x528,0x57e,0x5d4,0x62a,0x680,0x6d6,0x72c,0x782,0x7d8 },
{ 0x01f,0x075,0x0cb,0x121,0x177,0x1cd,0x223,0x279,0x2cf,0x325,0x37b,0x3d1,0x427,0x47d,0x4d3,0x529,0x57f,0x5d5,0x62b,0x681,0x6d7,0x72d,0x783,0x7d9 },
{ 0x020,0x076,0x0cc,0x122,0x178,0x1ce,0x224,0x27a,0x2d0,0x326,0x37c,0x3d2,0x428,0x47e,0x4d4,0x52a,0x580,0x5d6,0x62c,0x682,0x6d8,0x72e,0x784,0x7da },
{ 0x021,0x077,0x0cd,0x123,0x179,0x1cf,0x225,0x27b,0x2d1,0x327,0x37d,0x3d3,0x429,0x47f,0x4d5,0x52b,0x581,0x5d7,0x62d,0x683,0x6d9,0x72f,0x785,0x7db },
{ 0x022,0x078,0x0ce,0x124,0x17a,0x1d0,0x226,0x27c,0x2d2,0x328,0x37e,0x3d4,0x42a,0x480,0x4d6,0x52c,0x582,0x5d8,0x62e,0x684,0x6da,0x730,0x786,0x7dc },
{ 0x023,0x079,0x0cf,0x125,0x17b,0x1d1,0x227,0x27d,0x2d3,0x329,0x37f,0x3d5,0x42b,0x481,0x4d7,0x52d,0x583,0x5d9,0x62f,0x685,0x6db,0x731,0x787,0x7dd },
{ 0x024,0x07a,0x0d0,0x126,0x17c,0x1d2,0x228,0x27e,0x2d4,0x32a,0x380,0x3d6,0x42c,0x482,0x4d8,0x52e,0x584,0x5da,0x630,0x686,0x6dc,0x732,0x788,0x7de },
{ 0x025,0x07b,0x0d1,0x127,0x17d,0x1d3,0x229,0x27f,0x2d5,0x32b,0x381,0x3d7,0x42d,0x483,0x4d9,0x52f,0x585,0x5db,0x631,0x687,0x6dd,0x733,0x789,0x7df },
{ 0x026,0x07c,0x0d2,0x128,0x17e,0x1d4,0x22a,0x280,0x2d6,0x32c,0x382,0x3d8,0x42e,0x484,0x4da,0x530,0x586,0x5dc,0x632,0x688,0x6de,0x734,0x78a,0x7e0 },
{ 0x027,0x07d,0x0d3,0x129,0x17f,0x1d5,0x22b,0x281,0x2d7,0x32d,0x383,0x3d9,0x42f,0x485,0x4db,0x531,0x587,0x5dd,0x633,0x689,0x6df,0x735,0x78b,0x7e1 },
{ 0x028,0x07e,0x0d4,0x12a,0x180,0x1d6,0x22c,0x282,0x2d8,0x32e,0x384,0x3da,0x430,0x486,0x4dc,0x532,0x588,0x5de,0x634,0x68a,0x6e0,0x736,0x78c,0x7e2 },
{ 0x029,0x07f,0x0d5,0x12b,0x181,0x1d7,0x22d,0x283,0x2d9,0x32f,0x385,0x3db,0x431,0x487,0x4dd,0x533,0x589,0x5df,0x635,0x68b,0x6e1,0x737,0x78d,0x7e3 },
{ 0x02a,0x080,0x0d6,0x12c,0x182,0x1d8,0x22e,0x284,0x2da,0x330,0x386,0x3dc,0x432,0x488,0x4de,0x534,0x58a,0x5e0,0x636,0x68c,0x6e2,0x738,0x78e,0x7e4 },
{ 0x02b,0x081,0x0d7,0x12d,0x183,0x1d9,0x22f,0x285,0x2db,0x331,0x387,0x3dd,0x433,0x489,0x4df,0x535,0x58b,0x5e1,0x637,0x68d,0x6e3,0x739,0x78f,0x7e5 },
{ 0x02c,0x082,0x0d8,0x12e,0x184,0x1da,0x230,0x286,0x2dc,0x332,0x388,0x3de,0x434,0x48a,0x4e0,0x536,0x58c,0x5e2,0x638,0x68e,0x6e4,0x73a,0x790,0x7e6 },
{ 0x02d,0x083,0x0d9,0x12f,0x185,0x1db,0x231,0x287,0x2dd,0x333,0x389,0x3df,0x435,0x48b,0x4e1,0x537,0x58d,0x5e3,0x639,0x68f,0x6e5,0x73b,0x791,0x7e7 },
{ 0x02e,0x084,0x0da,0x130,0x186,0x1dc,0x232,0x288,0x2de,0x334,0x38a,0x3e0,0x436,0x48c,0x4e2,0x538,0x58e,0x5e4,0x63a,0x690,0x6e6,0x73c,0x792,0x7e8 },
{ 0x02f,0x085,0x0db,0x131,0x187,0x1dd,0x233,0x289,0x2df,0x335,0x38b,0x3e1,0x437,0x48d,0x4e3,0x539,0x58f,0x5e5,0x63b,0x691,0x6e7,0x73d,0x793,0x7e9 },
{ 0x030,0x086,0x0dc,0x132,0x188,0x1de,0x234,0x28a,0x2e0,0x336,0x38c,0x3e2,0x438,0x48e,0x4e4,0x53a,0x590,0x5e6,0x63c,0x692,0x6e8,0x73e,0x794,0x7ea },
{ 0x031,0x087,0x0dd,0x133,0x189,0x1df,0x235,0x28b,0x2e1,0x337,0x38d,0x3e3,0x439,0x48f,0x4e5,0x53b,0x591,0x5e7,0x63d,0x693,0x6e9,0x73f,0x795,0x7eb },
{ 0x032,0x088,0x0de,0x134,0x18a,0x1e0,0x236,0x28c,0x2e2,0x338,0x38e,0x3e4,0x43a,0x490,0x4e6,0x53c,0x592,0x5e8,0x63e,0x694,0x6ea,0x740,0x796,0x7ec },
{ 0x033,0x089,0x0df,0x135,0x18b,0x1e1,0x237,0x28d,0x2e3,0x339,0x38f,0x3e5,0x43b,0x491,0x4e7,0x53d,0x593,0x5e9,0x63f,0x695,0x6eb,0x741,0x797,0x7ed },
{ 0x034,0x08a,0x0e0,0x136,0x18c,0x1e2,0x238,0x28e,0x2e4,0x33a,0x390,0x3e6,0x43c,0x492,0x4e8,0x53e,0x594,0x5ea,0x640,0x696,0x6ec,0x742,0x798,0x7ee },
{ 0x035,0x08b,0x0e1,0x137,0x18d,0x1e3,0x239,0x28f,0x2e5,0x33b,0x391,0x3e7,0x43d,0x493,0x4e9,0x53f,0x595,0x5eb,0x641,0x697,0x6ed,0x743,0x799,0x7ef },
{ 0x036,0x08c,0x0e2,0x138,0x18e,0x1e4,0x23a,0x290,0x2e6,0x33c,0x392,0x3e8,0x43e,0x494,0x4ea,0x540,0x596,0x5ec,0x642,0x698,0x6ee,0x744,0x79a,0x7f0 },
{ 0x037,0x08d,0x0e3,0x139,0x18f,0x1e5,0x23b,0x291,0x2e7,0x33d,0x393,0x3e9,0x43f,0x495,0x4eb,0x541,0x597,0x5ed,0x643,0x699,0x6ef,0x745,0x79b,0x7f1 },
{ 0x038,0x08e,0x0e4,0x13a,0x190,0x1e6,0x23c,0x292,0x2e8,0x33e,0x394,0x3ea,0x440,0x496,0x4ec,0x542,0x598,0x5ee,0x644,0x69a,0x6f0,0x746,0x79c,0x7f2 },
{ 0x039,0x08f,0x0e5,0x13b,0x191,0x1e7,0x23d,0x293,0x2e9,0x33f,0x395,0x3eb,0x441,0x497,0x4ed,0x543,0x599,0x5ef,0x645,0x69b,0x6f1,0x747,0x79d,0x7f3 },
{ 0x03a,0x090,0x0e6,0x13c,0x192,0x1e8,0x23e,0x294,0x2ea,0x340,0x396,0x3ec,0x442,0x498,0x4ee,0x544,0x59a,0x5f0,0x646,0x69c,0x6f2,0x748,0x79e,0x7f4 },
{ 0x03b,0x091,0x0e7,0x13d,0x193,0x1e9,0x23f,0x295,0x2eb,0x341,0x397,0x3ed,0x443,0x499,0x4ef,0x545,0x59b,0x5f1,0x647,0x69d,0x6f3,0x749,0x79f,0x7f5 },
{ 0x03c,0x092,0x0e8,0x13e,0x194,0x1ea,0x240,0x296,0x2ec,0x342,0x398,0x3ee,0x444,0x49a,0x4f0,0x546,0x59c,0x5f2,0x648,0x69e,0x6f4,0x74a,0x7a0,0x7f6 },
{ 0x03d,0x093,0x0e9,0x13f,0x195,0x1eb,0x241,0x297,0x2ed,0x343,0x399,0x3ef,0x445,0x49b,0x4f1,0x547,0x59d,0x5f3,0x649,0x69f,0x6f5,0x74b,0x7a1,0x7f7 },
{ 0x03e,0x094,0x0ea,0x140,0x196,0x1ec,0x242,0x298,0x2ee,0x344,0x39a,0x3f0,0x446,0x49c,0x4f2,0x548,0x59e,0x5f4,0x64a,0x6a0,0x6f6,0x74c,0x7a2,0x7f8 },
{ 0x03f,0x095,0x0eb,0x141,0x197,0x1ed,0x243,0x299,0x2ef,0x345,0x39b,0x3f1,0x447,0x49d,0x4f3,0x549,0x59f,0x5f5,0x64b,0x6a1,0x6f7,0x74d,0x7a3,0x7f9 },
{ 0x040,0x096,0x0ec,0x142,0x198,0x1ee,0x244,0x29a,0x2f0,0x346,0x39c,0x3f2,0x448,0x49e,0x4f4,0x54a,0x5a0,0x5f6,0x64c,0x6a2,0x6f8,0x74e,0x7a4,0x7fa },
{ 0x041,0x097,0x0ed,0x143,0x199,0x1ef,0x245,0x29b,0x2f1,0x347,0x39d,0x3f3,0x449,0x49f,0x4f5,0x54b,0x5a1,0x5f7,0x64d,0x6a3,0x6f9,0x74f,0x7a5,0x7fb },
{ 0x042,0x098,0x0ee,0x144,0x19a,0x1f0,0x246,0x29c,0x2f2,0x348,0x39e,0x3f4,0x44a,0x4a0,0x4f6,0x54c,0x5a2,0x5f8,0x64e,0x6a4,0x6fa,0x750,0x7a6,0x7fc },
{ 0x043,0x099,0x0ef,0x145,0x19b,0x1f1,0x247,0x29d,0x2f3,0x349,0x39f,0x3f5,0x44b,0x4a1,0x4f7,0x54d,0x5a3,0x5f9,0x64f,0x6a5,0x6fb,0x751,0x7a7,0x7fd },
{ 0x044,0x09a,0x0f0,0x146,0x19c,0x1f2,0x248,0x29e,0x2f4,0x34a,0x3a0,0x3f6,0x44c,0x4a2,0x4f8,0x54e,0x5a4,0x5fa,0x650,0x6a6,0x6fc,0x752,0x7a8,0x7fe },
{ 0x045,0x09b,0x0f1,0x147,0x19d,0x1f3,0x249,0x29f,0x2f5,0x34b,0x3a1,0x3f7,0x44d,0x4a3,0x4f9,0x54f,0x5a5,0x5fb,0x651,0x6a7,0x6fd,0x753,0x7a9,0x7ff },
{ 0x046,0x09c,0x0f2,0x148,0x19e,0x1f4,0x24a,0x2a0,0x2f6,0x34c,0x3a2,0x3f8,0x44e,0x4a4,0x4fa,0x550,0x5a6,0x5fc,0x652,0x6a8,0x6fe,0x754,0x7aa,0x800 },
{ 0x047,0x09d,0x0f3,0x149,0x19f,0x1f5,0x24b,0x2a1,0x2f7,0x34d,0x3a3,0x3f9,0x44f,0x4a5,0x4fb,0x551,0x5a7,0x5fd,0x653,0x6a9,0x6ff,0x755,0x7ab,0x801 },
{ 0x048,0x09e,0x0f4,0x14a,0x1a0,0x1f6,0x24c,0x2a2,0x2f8,0x34e,0x3a4,0x3fa,0x450,0x4a6,0x4fc,0x552,0x5a8,0x5fe,0x654,0x6aa,0x700,0x756,0x7ac,0x802 },
{ 0x049,0x09f,0x0f5,0x14b,0x1a1,0x1f7,0x24d,0x2a3,0x2f9,0x34f,0x3a5,0x3fb,0x451,0x4a7,0x4fd,0x553,0x5a9,0x5ff,0x655,0x6ab,0x701,0x757,0x7ad,0x803 },
{ 0x04a,0x0a0,0x0f6,0x14c,0x1a2,0x1f8,0x24e,0x2a4,0x2fa,0x350,0x3a6,0x3fc,0x452,0x4a8,0x4fe,0x554,0x5aa,0x600,0x656,0x6ac,0x702,0x758,0x7ae,0x804 },
{ 0x04b,0x0a1,0x0f7,0x14d,0x1a3,0x1f9,0x24f,0x2a5,0x2fb,0x351,0x3a7,0x3fd,0x453,0x4a9,0x4ff,0x555,0x5ab,0x601,0x657,0x6ad,0x703,0x759,0x7af,0x805 },
{ 0x04c,0x0a2,0x0f8,0x14e,0x1a4,0x1fa,0x250,0x2a6,0x2fc,0x352,0x3a8,0x3fe,0x454,0x4aa,0x500,0x556,0x5ac,0x602,0x658,0x6ae,0x704,0x75a,0x7b0,0x806 },
{ 0x04d,0x0a3,0x0f9,0x14f,0x1a5,0x1fb,0x251,0x2a7,0x2fd,0x353,0x3a9,0x3ff,0x455,0x4ab,0x501,0x557,0x5ad,0x603,0x659,0x6af,0x705,0x75b,0x7b1,0x807 },
{ 0x04e,0x0a4,0x0fa,0x150,0x1a6,0x1fc,0x252,0x2a8,0x2fe,0x354,0x3aa,0x400,0x456,0x4ac,0x502,0x558,0x5ae,0x604,0x65a,0x6b0,0x706,0x75c,0x7b2,0x808 },
{ 0x04f,0x0a5,0x0fb,0x151,0x1a7,0x1fd,0x253,0x2a9,0x2ff,0x355,0x3ab,0x401,0x457,0x4ad,0x503,0x559,0x5af,0x605,0x65b,0x6b1,0x707,0x75d,0x7b3,0x809 },
{ 0x050,0x0a6,0x0fc,0x152,0x1a8,0x1fe,0x254,0x2aa,0x300,0x356,0x3ac,0x402,0x458,0x4ae,0x504,0x55a,0x5b0,0x606,0x65c,0x6b2,0x708,0x75e,0x7b4,0x80a },
{ 0x051,0x0a7,0x0fd,0x153,0x1a9,0x1ff,0x255,0x2ab,0x301,0x357,0x3ad,0x403,0x459,0x4af,0x505,0x55b,0x5b1,0x607,0x65d,0x6b3,0x709,0x75f,0x7b5,0x80b },
{ 0x052,0x0a8,0x0fe,0x154,0x1aa,0x200,0x256,0x2ac,0x302,0x358,0x3ae,0x404,0x45a,0x4b0,0x506,0x55c,0x5b2,0x608,0x65e,0x6b4,0x70a,0x760,0x7b6,0x80c },
{ 0x053,0x0a9,0x0ff,0x155,0x1ab,0x201,0x257,0x2ad,0x303,0x359,0x3af,0x405,0x45b,0x4b1,0x507,0x55d,0x5b3,0x609,0x65f,0x6b5,0x70b,0x761,0x7b7,0x80d },
{ 0x054,0x0aa,0x100,0x156,0x1ac,0x202,0x258,0x2ae,0x304,0x35a,0x3b0,0x406,0x45c,0x4b2,0x508,0x55e,0x5b4,0x60a,0x660,0x6b6,0x70c,0x762,0x7b8,0x80e },
{ 0x055,0x0ab,0x101,0x157,0x1ad,0x203,0x259,0x2af,0x305,0x35b,0x3b1,0x407,0x45d,0x4b3,0x509,0x55f,0x5b5,0x60b,0x661,0x6b7,0x70d,0x763,0x7b9,0x80f }
};
/**
* @brief -------------------------------------------------
* qoffsets - each row represents the addresses used to calculate a byte of the ECC Q
* data 52 (*2) ECC Q bytes, 43 values represented by each
* -------------------------------------------------.
*/
static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
{
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
{ 0x056,0x0ae,0x106,0x15e,0x1b6,0x20e,0x266,0x2be,0x316,0x36e,0x3c6,0x41e,0x476,0x4ce,0x526,0x57e,0x5d6,0x62e,0x686,0x6de,0x736,0x78e,0x7e6,0x83e,0x896,0x032,0x08a,0x0e2,0x13a,0x192,0x1ea,0x242,0x29a,0x2f2,0x34a,0x3a2,0x3fa,0x452,0x4aa,0x502,0x55a,0x5b2,0x60a },
{ 0x057,0x0af,0x107,0x15f,0x1b7,0x20f,0x267,0x2bf,0x317,0x36f,0x3c7,0x41f,0x477,0x4cf,0x527,0x57f,0x5d7,0x62f,0x687,0x6df,0x737,0x78f,0x7e7,0x83f,0x897,0x033,0x08b,0x0e3,0x13b,0x193,0x1eb,0x243,0x29b,0x2f3,0x34b,0x3a3,0x3fb,0x453,0x4ab,0x503,0x55b,0x5b3,0x60b },
{ 0x0ac,0x104,0x15c,0x1b4,0x20c,0x264,0x2bc,0x314,0x36c,0x3c4,0x41c,0x474,0x4cc,0x524,0x57c,0x5d4,0x62c,0x684,0x6dc,0x734,0x78c,0x7e4,0x83c,0x894,0x030,0x088,0x0e0,0x138,0x190,0x1e8,0x240,0x298,0x2f0,0x348,0x3a0,0x3f8,0x450,0x4a8,0x500,0x558,0x5b0,0x608,0x660 },
{ 0x0ad,0x105,0x15d,0x1b5,0x20d,0x265,0x2bd,0x315,0x36d,0x3c5,0x41d,0x475,0x4cd,0x525,0x57d,0x5d5,0x62d,0x685,0x6dd,0x735,0x78d,0x7e5,0x83d,0x895,0x031,0x089,0x0e1,0x139,0x191,0x1e9,0x241,0x299,0x2f1,0x349,0x3a1,0x3f9,0x451,0x4a9,0x501,0x559,0x5b1,0x609,0x661 },
{ 0x102,0x15a,0x1b2,0x20a,0x262,0x2ba,0x312,0x36a,0x3c2,0x41a,0x472,0x4ca,0x522,0x57a,0x5d2,0x62a,0x682,0x6da,0x732,0x78a,0x7e2,0x83a,0x892,0x02e,0x086,0x0de,0x136,0x18e,0x1e6,0x23e,0x296,0x2ee,0x346,0x39e,0x3f6,0x44e,0x4a6,0x4fe,0x556,0x5ae,0x606,0x65e,0x6b6 },
{ 0x103,0x15b,0x1b3,0x20b,0x263,0x2bb,0x313,0x36b,0x3c3,0x41b,0x473,0x4cb,0x523,0x57b,0x5d3,0x62b,0x683,0x6db,0x733,0x78b,0x7e3,0x83b,0x893,0x02f,0x087,0x0df,0x137,0x18f,0x1e7,0x23f,0x297,0x2ef,0x347,0x39f,0x3f7,0x44f,0x4a7,0x4ff,0x557,0x5af,0x607,0x65f,0x6b7 },
{ 0x158,0x1b0,0x208,0x260,0x2b8,0x310,0x368,0x3c0,0x418,0x470,0x4c8,0x520,0x578,0x5d0,0x628,0x680,0x6d8,0x730,0x788,0x7e0,0x838,0x890,0x02c,0x084,0x0dc,0x134,0x18c,0x1e4,0x23c,0x294,0x2ec,0x344,0x39c,0x3f4,0x44c,0x4a4,0x4fc,0x554,0x5ac,0x604,0x65c,0x6b4,0x70c },
{ 0x159,0x1b1,0x209,0x261,0x2b9,0x311,0x369,0x3c1,0x419,0x471,0x4c9,0x521,0x579,0x5d1,0x629,0x681,0x6d9,0x731,0x789,0x7e1,0x839,0x891,0x02d,0x085,0x0dd,0x135,0x18d,0x1e5,0x23d,0x295,0x2ed,0x345,0x39d,0x3f5,0x44d,0x4a5,0x4fd,0x555,0x5ad,0x605,0x65d,0x6b5,0x70d },
{ 0x1ae,0x206,0x25e,0x2b6,0x30e,0x366,0x3be,0x416,0x46e,0x4c6,0x51e,0x576,0x5ce,0x626,0x67e,0x6d6,0x72e,0x786,0x7de,0x836,0x88e,0x02a,0x082,0x0da,0x132,0x18a,0x1e2,0x23a,0x292,0x2ea,0x342,0x39a,0x3f2,0x44a,0x4a2,0x4fa,0x552,0x5aa,0x602,0x65a,0x6b2,0x70a,0x762 },
{ 0x1af,0x207,0x25f,0x2b7,0x30f,0x367,0x3bf,0x417,0x46f,0x4c7,0x51f,0x577,0x5cf,0x627,0x67f,0x6d7,0x72f,0x787,0x7df,0x837,0x88f,0x02b,0x083,0x0db,0x133,0x18b,0x1e3,0x23b,0x293,0x2eb,0x343,0x39b,0x3f3,0x44b,0x4a3,0x4fb,0x553,0x5ab,0x603,0x65b,0x6b3,0x70b,0x763 },
{ 0x204,0x25c,0x2b4,0x30c,0x364,0x3bc,0x414,0x46c,0x4c4,0x51c,0x574,0x5cc,0x624,0x67c,0x6d4,0x72c,0x784,0x7dc,0x834,0x88c,0x028,0x080,0x0d8,0x130,0x188,0x1e0,0x238,0x290,0x2e8,0x340,0x398,0x3f0,0x448,0x4a0,0x4f8,0x550,0x5a8,0x600,0x658,0x6b0,0x708,0x760,0x7b8 },
{ 0x205,0x25d,0x2b5,0x30d,0x365,0x3bd,0x415,0x46d,0x4c5,0x51d,0x575,0x5cd,0x625,0x67d,0x6d5,0x72d,0x785,0x7dd,0x835,0x88d,0x029,0x081,0x0d9,0x131,0x189,0x1e1,0x239,0x291,0x2e9,0x341,0x399,0x3f1,0x449,0x4a1,0x4f9,0x551,0x5a9,0x601,0x659,0x6b1,0x709,0x761,0x7b9 },
{ 0x25a,0x2b2,0x30a,0x362,0x3ba,0x412,0x46a,0x4c2,0x51a,0x572,0x5ca,0x622,0x67a,0x6d2,0x72a,0x782,0x7da,0x832,0x88a,0x026,0x07e,0x0d6,0x12e,0x186,0x1de,0x236,0x28e,0x2e6,0x33e,0x396,0x3ee,0x446,0x49e,0x4f6,0x54e,0x5a6,0x5fe,0x656,0x6ae,0x706,0x75e,0x7b6,0x80e },
{ 0x25b,0x2b3,0x30b,0x363,0x3bb,0x413,0x46b,0x4c3,0x51b,0x573,0x5cb,0x623,0x67b,0x6d3,0x72b,0x783,0x7db,0x833,0x88b,0x027,0x07f,0x0d7,0x12f,0x187,0x1df,0x237,0x28f,0x2e7,0x33f,0x397,0x3ef,0x447,0x49f,0x4f7,0x54f,0x5a7,0x5ff,0x657,0x6af,0x707,0x75f,0x7b7,0x80f },
{ 0x2b0,0x308,0x360,0x3b8,0x410,0x468,0x4c0,0x518,0x570,0x5c8,0x620,0x678,0x6d0,0x728,0x780,0x7d8,0x830,0x888,0x024,0x07c,0x0d4,0x12c,0x184,0x1dc,0x234,0x28c,0x2e4,0x33c,0x394,0x3ec,0x444,0x49c,0x4f4,0x54c,0x5a4,0x5fc,0x654,0x6ac,0x704,0x75c,0x7b4,0x80c,0x864 },
{ 0x2b1,0x309,0x361,0x3b9,0x411,0x469,0x4c1,0x519,0x571,0x5c9,0x621,0x679,0x6d1,0x729,0x781,0x7d9,0x831,0x889,0x025,0x07d,0x0d5,0x12d,0x185,0x1dd,0x235,0x28d,0x2e5,0x33d,0x395,0x3ed,0x445,0x49d,0x4f5,0x54d,0x5a5,0x5fd,0x655,0x6ad,0x705,0x75d,0x7b5,0x80d,0x865 },
{ 0x306,0x35e,0x3b6,0x40e,0x466,0x4be,0x516,0x56e,0x5c6,0x61e,0x676,0x6ce,0x726,0x77e,0x7d6,0x82e,0x886,0x022,0x07a,0x0d2,0x12a,0x182,0x1da,0x232,0x28a,0x2e2,0x33a,0x392,0x3ea,0x442,0x49a,0x4f2,0x54a,0x5a2,0x5fa,0x652,0x6aa,0x702,0x75a,0x7b2,0x80a,0x862,0x8ba },
{ 0x307,0x35f,0x3b7,0x40f,0x467,0x4bf,0x517,0x56f,0x5c7,0x61f,0x677,0x6cf,0x727,0x77f,0x7d7,0x82f,0x887,0x023,0x07b,0x0d3,0x12b,0x183,0x1db,0x233,0x28b,0x2e3,0x33b,0x393,0x3eb,0x443,0x49b,0x4f3,0x54b,0x5a3,0x5fb,0x653,0x6ab,0x703,0x75b,0x7b3,0x80b,0x863,0x8bb },
{ 0x35c,0x3b4,0x40c,0x464,0x4bc,0x514,0x56c,0x5c4,0x61c,0x674,0x6cc,0x724,0x77c,0x7d4,0x82c,0x884,0x020,0x078,0x0d0,0x128,0x180,0x1d8,0x230,0x288,0x2e0,0x338,0x390,0x3e8,0x440,0x498,0x4f0,0x548,0x5a0,0x5f8,0x650,0x6a8,0x700,0x758,0x7b0,0x808,0x860,0x8b8,0x054 },
{ 0x35d,0x3b5,0x40d,0x465,0x4bd,0x515,0x56d,0x5c5,0x61d,0x675,0x6cd,0x725,0x77d,0x7d5,0x82d,0x885,0x021,0x079,0x0d1,0x129,0x181,0x1d9,0x231,0x289,0x2e1,0x339,0x391,0x3e9,0x441,0x499,0x4f1,0x549,0x5a1,0x5f9,0x651,0x6a9,0x701,0x759,0x7b1,0x809,0x861,0x8b9,0x055 },
{ 0x3b2,0x40a,0x462,0x4ba,0x512,0x56a,0x5c2,0x61a,0x672,0x6ca,0x722,0x77a,0x7d2,0x82a,0x882,0x01e,0x076,0x0ce,0x126,0x17e,0x1d6,0x22e,0x286,0x2de,0x336,0x38e,0x3e6,0x43e,0x496,0x4ee,0x546,0x59e,0x5f6,0x64e,0x6a6,0x6fe,0x756,0x7ae,0x806,0x85e,0x8b6,0x052,0x0aa },
{ 0x3b3,0x40b,0x463,0x4bb,0x513,0x56b,0x5c3,0x61b,0x673,0x6cb,0x723,0x77b,0x7d3,0x82b,0x883,0x01f,0x077,0x0cf,0x127,0x17f,0x1d7,0x22f,0x287,0x2df,0x337,0x38f,0x3e7,0x43f,0x497,0x4ef,0x547,0x59f,0x5f7,0x64f,0x6a7,0x6ff,0x757,0x7af,0x807,0x85f,0x8b7,0x053,0x0ab },
{ 0x408,0x460,0x4b8,0x510,0x568,0x5c0,0x618,0x670,0x6c8,0x720,0x778,0x7d0,0x828,0x880,0x01c,0x074,0x0cc,0x124,0x17c,0x1d4,0x22c,0x284,0x2dc,0x334,0x38c,0x3e4,0x43c,0x494,0x4ec,0x544,0x59c,0x5f4,0x64c,0x6a4,0x6fc,0x754,0x7ac,0x804,0x85c,0x8b4,0x050,0x0a8,0x100 },
{ 0x409,0x461,0x4b9,0x511,0x569,0x5c1,0x619,0x671,0x6c9,0x721,0x779,0x7d1,0x829,0x881,0x01d,0x075,0x0cd,0x125,0x17d,0x1d5,0x22d,0x285,0x2dd,0x335,0x38d,0x3e5,0x43d,0x495,0x4ed,0x545,0x59d,0x5f5,0x64d,0x6a5,0x6fd,0x755,0x7ad,0x805,0x85d,0x8b5,0x051,0x0a9,0x101 },
{ 0x45e,0x4b6,0x50e,0x566,0x5be,0x616,0x66e,0x6c6,0x71e,0x776,0x7ce,0x826,0x87e,0x01a,0x072,0x0ca,0x122,0x17a,0x1d2,0x22a,0x282,0x2da,0x332,0x38a,0x3e2,0x43a,0x492,0x4ea,0x542,0x59a,0x5f2,0x64a,0x6a2,0x6fa,0x752,0x7aa,0x802,0x85a,0x8b2,0x04e,0x0a6,0x0fe,0x156 },
{ 0x45f,0x4b7,0x50f,0x567,0x5bf,0x617,0x66f,0x6c7,0x71f,0x777,0x7cf,0x827,0x87f,0x01b,0x073,0x0cb,0x123,0x17b,0x1d3,0x22b,0x283,0x2db,0x333,0x38b,0x3e3,0x43b,0x493,0x4eb,0x543,0x59b,0x5f3,0x64b,0x6a3,0x6fb,0x753,0x7ab,0x803,0x85b,0x8b3,0x04f,0x0a7,0x0ff,0x157 },
{ 0x4b4,0x50c,0x564,0x5bc,0x614,0x66c,0x6c4,0x71c,0x774,0x7cc,0x824,0x87c,0x018,0x070,0x0c8,0x120,0x178,0x1d0,0x228,0x280,0x2d8,0x330,0x388,0x3e0,0x438,0x490,0x4e8,0x540,0x598,0x5f0,0x648,0x6a0,0x6f8,0x750,0x7a8,0x800,0x858,0x8b0,0x04c,0x0a4,0x0fc,0x154,0x1ac },
{ 0x4b5,0x50d,0x565,0x5bd,0x615,0x66d,0x6c5,0x71d,0x775,0x7cd,0x825,0x87d,0x019,0x071,0x0c9,0x121,0x179,0x1d1,0x229,0x281,0x2d9,0x331,0x389,0x3e1,0x439,0x491,0x4e9,0x541,0x599,0x5f1,0x649,0x6a1,0x6f9,0x751,0x7a9,0x801,0x859,0x8b1,0x04d,0x0a5,0x0fd,0x155,0x1ad },
{ 0x50a,0x562,0x5ba,0x612,0x66a,0x6c2,0x71a,0x772,0x7ca,0x822,0x87a,0x016,0x06e,0x0c6,0x11e,0x176,0x1ce,0x226,0x27e,0x2d6,0x32e,0x386,0x3de,0x436,0x48e,0x4e6,0x53e,0x596,0x5ee,0x646,0x69e,0x6f6,0x74e,0x7a6,0x7fe,0x856,0x8ae,0x04a,0x0a2,0x0fa,0x152,0x1aa,0x202 },
{ 0x50b,0x563,0x5bb,0x613,0x66b,0x6c3,0x71b,0x773,0x7cb,0x823,0x87b,0x017,0x06f,0x0c7,0x11f,0x177,0x1cf,0x227,0x27f,0x2d7,0x32f,0x387,0x3df,0x437,0x48f,0x4e7,0x53f,0x597,0x5ef,0x647,0x69f,0x6f7,0x74f,0x7a7,0x7ff,0x857,0x8af,0x04b,0x0a3,0x0fb,0x153,0x1ab,0x203 },
{ 0x560,0x5b8,0x610,0x668,0x6c0,0x718,0x770,0x7c8,0x820,0x878,0x014,0x06c,0x0c4,0x11c,0x174,0x1cc,0x224,0x27c,0x2d4,0x32c,0x384,0x3dc,0x434,0x48c,0x4e4,0x53c,0x594,0x5ec,0x644,0x69c,0x6f4,0x74c,0x7a4,0x7fc,0x854,0x8ac,0x048,0x0a0,0x0f8,0x150,0x1a8,0x200,0x258 },
{ 0x561,0x5b9,0x611,0x669,0x6c1,0x719,0x771,0x7c9,0x821,0x879,0x015,0x06d,0x0c5,0x11d,0x175,0x1cd,0x225,0x27d,0x2d5,0x32d,0x385,0x3dd,0x435,0x48d,0x4e5,0x53d,0x595,0x5ed,0x645,0x69d,0x6f5,0x74d,0x7a5,0x7fd,0x855,0x8ad,0x049,0x0a1,0x0f9,0x151,0x1a9,0x201,0x259 },
{ 0x5b6,0x60e,0x666,0x6be,0x716,0x76e,0x7c6,0x81e,0x876,0x012,0x06a,0x0c2,0x11a,0x172,0x1ca,0x222,0x27a,0x2d2,0x32a,0x382,0x3da,0x432,0x48a,0x4e2,0x53a,0x592,0x5ea,0x642,0x69a,0x6f2,0x74a,0x7a2,0x7fa,0x852,0x8aa,0x046,0x09e,0x0f6,0x14e,0x1a6,0x1fe,0x256,0x2ae },
{ 0x5b7,0x60f,0x667,0x6bf,0x717,0x76f,0x7c7,0x81f,0x877,0x013,0x06b,0x0c3,0x11b,0x173,0x1cb,0x223,0x27b,0x2d3,0x32b,0x383,0x3db,0x433,0x48b,0x4e3,0x53b,0x593,0x5eb,0x643,0x69b,0x6f3,0x74b,0x7a3,0x7fb,0x853,0x8ab,0x047,0x09f,0x0f7,0x14f,0x1a7,0x1ff,0x257,0x2af },
{ 0x60c,0x664,0x6bc,0x714,0x76c,0x7c4,0x81c,0x874,0x010,0x068,0x0c0,0x118,0x170,0x1c8,0x220,0x278,0x2d0,0x328,0x380,0x3d8,0x430,0x488,0x4e0,0x538,0x590,0x5e8,0x640,0x698,0x6f0,0x748,0x7a0,0x7f8,0x850,0x8a8,0x044,0x09c,0x0f4,0x14c,0x1a4,0x1fc,0x254,0x2ac,0x304 },
{ 0x60d,0x665,0x6bd,0x715,0x76d,0x7c5,0x81d,0x875,0x011,0x069,0x0c1,0x119,0x171,0x1c9,0x221,0x279,0x2d1,0x329,0x381,0x3d9,0x431,0x489,0x4e1,0x539,0x591,0x5e9,0x641,0x699,0x6f1,0x749,0x7a1,0x7f9,0x851,0x8a9,0x045,0x09d,0x0f5,0x14d,0x1a5,0x1fd,0x255,0x2ad,0x305 },
{ 0x662,0x6ba,0x712,0x76a,0x7c2,0x81a,0x872,0x00e,0x066,0x0be,0x116,0x16e,0x1c6,0x21e,0x276,0x2ce,0x326,0x37e,0x3d6,0x42e,0x486,0x4de,0x536,0x58e,0x5e6,0x63e,0x696,0x6ee,0x746,0x79e,0x7f6,0x84e,0x8a6,0x042,0x09a,0x0f2,0x14a,0x1a2,0x1fa,0x252,0x2aa,0x302,0x35a },
{ 0x663,0x6bb,0x713,0x76b,0x7c3,0x81b,0x873,0x00f,0x067,0x0bf,0x117,0x16f,0x1c7,0x21f,0x277,0x2cf,0x327,0x37f,0x3d7,0x42f,0x487,0x4df,0x537,0x58f,0x5e7,0x63f,0x697,0x6ef,0x747,0x79f,0x7f7,0x84f,0x8a7,0x043,0x09b,0x0f3,0x14b,0x1a3,0x1fb,0x253,0x2ab,0x303,0x35b },
{ 0x6b8,0x710,0x768,0x7c0,0x818,0x870,0x00c,0x064,0x0bc,0x114,0x16c,0x1c4,0x21c,0x274,0x2cc,0x324,0x37c,0x3d4,0x42c,0x484,0x4dc,0x534,0x58c,0x5e4,0x63c,0x694,0x6ec,0x744,0x79c,0x7f4,0x84c,0x8a4,0x040,0x098,0x0f0,0x148,0x1a0,0x1f8,0x250,0x2a8,0x300,0x358,0x3b0 },
{ 0x6b9,0x711,0x769,0x7c1,0x819,0x871,0x00d,0x065,0x0bd,0x115,0x16d,0x1c5,0x21d,0x275,0x2cd,0x325,0x37d,0x3d5,0x42d,0x485,0x4dd,0x535,0x58d,0x5e5,0x63d,0x695,0x6ed,0x745,0x79d,0x7f5,0x84d,0x8a5,0x041,0x099,0x0f1,0x149,0x1a1,0x1f9,0x251,0x2a9,0x301,0x359,0x3b1 },
{ 0x70e,0x766,0x7be,0x816,0x86e,0x00a,0x062,0x0ba,0x112,0x16a,0x1c2,0x21a,0x272,0x2ca,0x322,0x37a,0x3d2,0x42a,0x482,0x4da,0x532,0x58a,0x5e2,0x63a,0x692,0x6ea,0x742,0x79a,0x7f2,0x84a,0x8a2,0x03e,0x096,0x0ee,0x146,0x19e,0x1f6,0x24e,0x2a6,0x2fe,0x356,0x3ae,0x406 },
{ 0x70f,0x767,0x7bf,0x817,0x86f,0x00b,0x063,0x0bb,0x113,0x16b,0x1c3,0x21b,0x273,0x2cb,0x323,0x37b,0x3d3,0x42b,0x483,0x4db,0x533,0x58b,0x5e3,0x63b,0x693,0x6eb,0x743,0x79b,0x7f3,0x84b,0x8a3,0x03f,0x097,0x0ef,0x147,0x19f,0x1f7,0x24f,0x2a7,0x2ff,0x357,0x3af,0x407 },
{ 0x764,0x7bc,0x814,0x86c,0x008,0x060,0x0b8,0x110,0x168,0x1c0,0x218,0x270,0x2c8,0x320,0x378,0x3d0,0x428,0x480,0x4d8,0x530,0x588,0x5e0,0x638,0x690,0x6e8,0x740,0x798,0x7f0,0x848,0x8a0,0x03c,0x094,0x0ec,0x144,0x19c,0x1f4,0x24c,0x2a4,0x2fc,0x354,0x3ac,0x404,0x45c },
{ 0x765,0x7bd,0x815,0x86d,0x009,0x061,0x0b9,0x111,0x169,0x1c1,0x219,0x271,0x2c9,0x321,0x379,0x3d1,0x429,0x481,0x4d9,0x531,0x589,0x5e1,0x639,0x691,0x6e9,0x741,0x799,0x7f1,0x849,0x8a1,0x03d,0x095,0x0ed,0x145,0x19d,0x1f5,0x24d,0x2a5,0x2fd,0x355,0x3ad,0x405,0x45d },
{ 0x7ba,0x812,0x86a,0x006,0x05e,0x0b6,0x10e,0x166,0x1be,0x216,0x26e,0x2c6,0x31e,0x376,0x3ce,0x426,0x47e,0x4d6,0x52e,0x586,0x5de,0x636,0x68e,0x6e6,0x73e,0x796,0x7ee,0x846,0x89e,0x03a,0x092,0x0ea,0x142,0x19a,0x1f2,0x24a,0x2a2,0x2fa,0x352,0x3aa,0x402,0x45a,0x4b2 },
{ 0x7bb,0x813,0x86b,0x007,0x05f,0x0b7,0x10f,0x167,0x1bf,0x217,0x26f,0x2c7,0x31f,0x377,0x3cf,0x427,0x47f,0x4d7,0x52f,0x587,0x5df,0x637,0x68f,0x6e7,0x73f,0x797,0x7ef,0x847,0x89f,0x03b,0x093,0x0eb,0x143,0x19b,0x1f3,0x24b,0x2a3,0x2fb,0x353,0x3ab,0x403,0x45b,0x4b3 },
{ 0x810,0x868,0x004,0x05c,0x0b4,0x10c,0x164,0x1bc,0x214,0x26c,0x2c4,0x31c,0x374,0x3cc,0x424,0x47c,0x4d4,0x52c,0x584,0x5dc,0x634,0x68c,0x6e4,0x73c,0x794,0x7ec,0x844,0x89c,0x038,0x090,0x0e8,0x140,0x198,0x1f0,0x248,0x2a0,0x2f8,0x350,0x3a8,0x400,0x458,0x4b0,0x508 },
{ 0x811,0x869,0x005,0x05d,0x0b5,0x10d,0x165,0x1bd,0x215,0x26d,0x2c5,0x31d,0x375,0x3cd,0x425,0x47d,0x4d5,0x52d,0x585,0x5dd,0x635,0x68d,0x6e5,0x73d,0x795,0x7ed,0x845,0x89d,0x039,0x091,0x0e9,0x141,0x199,0x1f1,0x249,0x2a1,0x2f9,0x351,0x3a9,0x401,0x459,0x4b1,0x509 },
{ 0x866,0x002,0x05a,0x0b2,0x10a,0x162,0x1ba,0x212,0x26a,0x2c2,0x31a,0x372,0x3ca,0x422,0x47a,0x4d2,0x52a,0x582,0x5da,0x632,0x68a,0x6e2,0x73a,0x792,0x7ea,0x842,0x89a,0x036,0x08e,0x0e6,0x13e,0x196,0x1ee,0x246,0x29e,0x2f6,0x34e,0x3a6,0x3fe,0x456,0x4ae,0x506,0x55e },
{ 0x867,0x003,0x05b,0x0b3,0x10b,0x163,0x1bb,0x213,0x26b,0x2c3,0x31b,0x373,0x3cb,0x423,0x47b,0x4d3,0x52b,0x583,0x5db,0x633,0x68b,0x6e3,0x73b,0x793,0x7eb,0x843,0x89b,0x037,0x08f,0x0e7,0x13f,0x197,0x1ef,0x247,0x29f,0x2f7,0x34f,0x3a7,0x3ff,0x457,0x4af,0x507,0x55f }
};
/*-------------------------------------------------
* ecc_source_byte - return data from the sector
* at the given offset, masking anything
* particular to a mode
*-------------------------------------------------
*/
static CHDR_INLINE uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
{
/* in mode 2 always treat these as 0 bytes */
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
}
/**
* @fn void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
*
* @brief -------------------------------------------------
* ecc_compute_bytes - calculate an ECC value (P or Q)
* -------------------------------------------------.
*
* @param sector The sector.
* @param row The row.
* @param rowlen The rowlen.
* @param [in,out] val1 The first value.
* @param [in,out] val2 The second value.
*/
void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t *val1, uint8_t *val2)
{
int component;
*val1 = *val2 = 0;
for (component = 0; component < rowlen; component++)
{
*val1 ^= ecc_source_byte(sector, row[component]);
*val2 ^= ecc_source_byte(sector, row[component]);
*val1 = ecclow[*val1];
}
*val1 = ecchigh[ecclow[*val1] ^ *val2];
*val2 ^= *val1;
}
/**
* @fn int ecc_verify(const uint8_t *sector)
*
* @brief -------------------------------------------------
* ecc_verify - verify the P and Q ECC codes in a sector
* -------------------------------------------------.
*
* @param sector The sector.
*
* @return true if it succeeds, false if it fails.
*/
int ecc_verify(const uint8_t *sector)
{
int byte;
/* first verify P bytes */
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
{
uint8_t val1, val2;
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &val1, &val2);
if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2)
return 0;
}
/* then verify Q bytes */
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
{
uint8_t val1, val2;
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &val1, &val2);
if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2)
return 0;
}
return 1;
}
/**
* @fn void ecc_generate(uint8_t *sector)
*
* @brief -------------------------------------------------
* ecc_generate - generate the P and Q ECC codes for a sector, overwriting any
* existing codes
* -------------------------------------------------.
*
* @param [in,out] sector If non-null, the sector.
*/
void ecc_generate(uint8_t *sector)
{
int byte;
/* first verify P bytes */
for (byte = 0; byte < ECC_P_NUM_BYTES; byte++)
ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, &sector[ECC_P_OFFSET + byte], &sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]);
/* then verify Q bytes */
for (byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, &sector[ECC_Q_OFFSET + byte], &sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]);
}
/**
* @fn void ecc_clear(uint8_t *sector)
*
* @brief -------------------------------------------------
* ecc_clear - erase the ECC P and Q cods to 0 within a sector
* -------------------------------------------------.
*
* @param [in,out] sector If non-null, the sector.
*/
void ecc_clear(uint8_t *sector)
{
memset(&sector[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
memset(&sector[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
}
#endif /* WANT_RAW_DATA_SECTOR */
/* Handles decompression for CDZL, CDLZ, CDZS, and co. */
chd_error cd_codec_decompress(
uint8_t *buffer,
void *base_decompressor, chd_codec_interface_decompress base_decompress,
void *subcode_decompressor, chd_codec_interface_decompress subcode_decompress,
const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
uint32_t framenum;
chd_error decomp_err;
uint32_t complen_base;
#if !WANT_SUBCODE
(void)subcode_decompressor;
(void)subcode_decompress;
#endif
/* determine header bytes */
const uint32_t frames = destlen / CD_FRAME_SIZE;
const uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
const uint32_t ecc_bytes = (frames + 7) / 8;
const uint32_t header_bytes = ecc_bytes + complen_bytes;
/* input may be truncated, double-check */
if (complen < (ecc_bytes + 2))
return CHDERR_DECOMPRESSION_ERROR;
/* extract compressed length of base */
complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
if (complen_bytes > 2)
{
if (complen < (ecc_bytes + 3))
return CHDERR_DECOMPRESSION_ERROR;
complen_base = (complen_base << 8) | src[ecc_bytes + 2];
}
if (complen < (header_bytes + complen_base))
return CHDERR_DECOMPRESSION_ERROR;
/* reset and decode */
decomp_err = base_decompress(base_decompressor, &src[header_bytes], complen_base, &buffer[0], frames * CD_MAX_SECTOR_DATA);
if (decomp_err != CHDERR_NONE)
return decomp_err;
#if WANT_SUBCODE
decomp_err = subcode_decompress(subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
if (decomp_err != CHDERR_NONE)
return decomp_err;
#endif
/* reassemble the data */
for (framenum = 0; framenum < frames; framenum++)
{
#if WANT_RAW_DATA_SECTOR
uint8_t *sector;
#endif
memcpy(&dest[framenum * CD_FRAME_SIZE], &buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
#if WANT_SUBCODE
memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
#endif
#if WANT_RAW_DATA_SECTOR
/* reconstitute the ECC data and sync header */
sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE];
if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
{
memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));
ecc_generate(sector);
}
#endif
}
return CHDERR_NONE;
}
+2255
View File
File diff suppressed because it is too large. Load diff
+100
View File
@@ -0,0 +1,100 @@
#include "codec_cdfl.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "../include/libchdr/cdrom.h"
static uint32_t cdfl_codec_blocksize(uint32_t bytes)
{
/* for CDs it seems that CD_MAX_SECTOR_DATA is the right target */
uint32_t blocksize = bytes / 4;
while (blocksize > CD_MAX_SECTOR_DATA)
blocksize /= 2;
return blocksize;
}
chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes)
{
#if WANT_SUBCODE
chd_error ret;
#endif
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
/* make sure the CHD's hunk size is an even multiple of the frame size */
if (hunkbytes % CD_FRAME_SIZE != 0)
return CHDERR_CODEC_ERROR;
cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
if (cdfl->buffer == NULL)
return CHDERR_OUT_OF_MEMORY;
/* determine whether we want native or swapped samples */
cdfl->swap_endian = flac_decoder_detect_native_endian();
#if WANT_SUBCODE
/* init zlib inflater */
ret = zlib_codec_init(&cdfl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
if (ret != CHDERR_NONE)
return ret;
#endif
/* flac decoder init */
if (flac_decoder_init(&cdfl->decoder))
return CHDERR_OUT_OF_MEMORY;
return CHDERR_NONE;
}
void cdfl_codec_free(void *codec)
{
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
flac_decoder_free(&cdfl->decoder);
#if WANT_SUBCODE
zlib_codec_free(&cdfl->subcode_decompressor);
#endif
if (cdfl->buffer)
free(cdfl->buffer);
}
chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
uint32_t framenum;
uint8_t *buffer;
#if WANT_SUBCODE
uint32_t offset;
chd_error ret;
#endif
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
/* reset and decode */
uint32_t frames = destlen / CD_FRAME_SIZE;
if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen))
return CHDERR_DECOMPRESSION_ERROR;
buffer = &cdfl->buffer[0];
if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian))
return CHDERR_DECOMPRESSION_ERROR;
#if WANT_SUBCODE
/* inflate the subcode data */
offset = flac_decoder_finish(&cdfl->decoder);
ret = zlib_codec_decompress(&cdfl->subcode_decompressor, src + offset, complen - offset, &cdfl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
if (ret != CHDERR_NONE)
return ret;
#else
flac_decoder_finish(&cdfl->decoder);
#endif
/* reassemble the data */
for (framenum = 0; framenum < frames; framenum++)
{
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
#if WANT_SUBCODE
memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
#endif
}
return CHDERR_NONE;
}
+59
View File
@@ -0,0 +1,59 @@
#include "codec_cdlz.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "../include/libchdr/cdrom.h"
chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes)
{
chd_error ret;
cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
/* allocate buffer */
cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
if (cdlz->buffer == NULL)
return CHDERR_OUT_OF_MEMORY;
/* make sure the CHD's hunk size is an even multiple of the frame size */
ret = lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
if (ret != CHDERR_NONE)
return ret;
#if WANT_SUBCODE
ret = zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA);
if (ret != CHDERR_NONE)
return ret;
#endif
if (hunkbytes % CD_FRAME_SIZE != 0)
return CHDERR_CODEC_ERROR;
return CHDERR_NONE;
}
void cdlz_codec_free(void* codec)
{
cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
free(cdlz->buffer);
lzma_codec_free(&cdlz->base_decompressor);
#if WANT_SUBCODE
zlib_codec_free(&cdlz->subcode_decompressor);
#endif
}
chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
cdlz_codec_data* cdlz = (cdlz_codec_data*)codec;
return cd_codec_decompress(cdlz->buffer,
&cdlz->base_decompressor, lzma_codec_decompress,
#if WANT_SUBCODE
&cdlz->subcode_decompressor, zlib_codec_decompress,
#else
NULL, NULL,
#endif
src, complen, dest, destlen
);
}
+58
View File
@@ -0,0 +1,58 @@
#include "codec_cdzl.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "../include/libchdr/cdrom.h"
chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes)
{
chd_error ret;
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
/* make sure the CHD's hunk size is an even multiple of the frame size */
if (hunkbytes % CD_FRAME_SIZE != 0)
return CHDERR_CODEC_ERROR;
cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
if (cdzl->buffer == NULL)
return CHDERR_OUT_OF_MEMORY;
ret = zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
if (ret != CHDERR_NONE)
return ret;
#if WANT_SUBCODE
ret = zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA);
if (ret != CHDERR_NONE)
return ret;
#endif
return CHDERR_NONE;
}
void cdzl_codec_free(void *codec)
{
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
zlib_codec_free(&cdzl->base_decompressor);
#if WANT_SUBCODE
zlib_codec_free(&cdzl->subcode_decompressor);
#endif
free(cdzl->buffer);
}
chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
return cd_codec_decompress(cdzl->buffer,
&cdzl->base_decompressor, zlib_codec_decompress,
#if WANT_SUBCODE
&cdzl->subcode_decompressor, zlib_codec_decompress,
#else
NULL, NULL,
#endif
src, complen, dest, destlen
);
}
+59
View File
@@ -0,0 +1,59 @@
#include "codec_cdzs.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "../include/libchdr/cdrom.h"
chd_error cdzs_codec_init(void* codec, uint32_t hunkbytes)
{
chd_error ret;
cdzs_codec_data* cdzs = (cdzs_codec_data*) codec;
/* allocate buffer */
cdzs->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
if (cdzs->buffer == NULL)
return CHDERR_OUT_OF_MEMORY;
/* make sure the CHD's hunk size is an even multiple of the frame size */
ret = zstd_codec_init(&cdzs->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
if (ret != CHDERR_NONE)
return ret;
#if WANT_SUBCODE
ret = zstd_codec_init(&cdzs->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA);
if (ret != CHDERR_NONE)
return ret;
#endif
if (hunkbytes % CD_FRAME_SIZE != 0)
return CHDERR_CODEC_ERROR;
return CHDERR_NONE;
}
void cdzs_codec_free(void* codec)
{
cdzs_codec_data* cdzs = (cdzs_codec_data*) codec;
free(cdzs->buffer);
zstd_codec_free(&cdzs->base_decompressor);
#if WANT_SUBCODE
zstd_codec_free(&cdzs->subcode_decompressor);
#endif
}
chd_error cdzs_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
cdzs_codec_data* cdzs = (cdzs_codec_data*)codec;
return cd_codec_decompress(cdzs->buffer,
&cdzs->base_decompressor, zstd_codec_decompress,
#if WANT_SUBCODE
&cdzs->subcode_decompressor, zstd_codec_decompress,
#else
NULL, NULL,
#endif
src, complen, dest, destlen
);
}
+65
View File
@@ -0,0 +1,65 @@
#include "codec_flac.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/*------------------------------------------------------
* flac_codec_blocksize - return the optimal block size
*------------------------------------------------------
*/
static uint32_t flac_codec_blocksize(uint32_t bytes)
{
/* determine FLAC block size, which must be 16-65535
* clamp to 2k since that's supposed to be the sweet spot */
uint32_t blocksize = bytes / 4;
while (blocksize > 2048)
blocksize /= 2;
return blocksize;
}
chd_error flac_codec_init(void *codec, uint32_t hunkbytes)
{
flac_codec_data *flac = (flac_codec_data*)codec;
/* make sure the CHD's hunk size is an even multiple of the sample size */
if (hunkbytes % 4 != 0)
return CHDERR_CODEC_ERROR;
/* determine whether we want native or swapped samples */
flac->native_endian = flac_decoder_detect_native_endian();
/* flac decoder init */
if (flac_decoder_init(&flac->decoder))
return CHDERR_OUT_OF_MEMORY;
return CHDERR_NONE;
}
void flac_codec_free(void *codec)
{
flac_codec_data *flac = (flac_codec_data*)codec;
flac_decoder_free(&flac->decoder);
}
chd_error flac_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
flac_codec_data *flac = (flac_codec_data*)codec;
int swap_endian;
if (src[0] == 'L')
swap_endian = !flac->native_endian;
else if (src[0] == 'B')
swap_endian = flac->native_endian;
else
return CHDERR_DECOMPRESSION_ERROR;
if (!flac_decoder_reset(&flac->decoder, 44100, 2, flac_codec_blocksize(destlen), src + 1, complen - 1))
return CHDERR_DECOMPRESSION_ERROR;
if (!flac_decoder_decode_interleaved(&flac->decoder, (int16_t *)(dest), destlen/4, swap_endian))
return CHDERR_DECOMPRESSION_ERROR;
flac_decoder_finish(&flac->decoder);
return CHDERR_NONE;
}
+46
View File
@@ -0,0 +1,46 @@
#include "codec_huff.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "../include/libchdr/huffman.h"
chd_error huff_codec_init(void* codec, uint32_t hunkbytes)
{
huff_codec_data* huff_codec = (huff_codec_data*) codec;
(void)hunkbytes;
huff_codec->decoder = create_huffman_decoder(256, 16);
return CHDERR_NONE;
}
void huff_codec_free(void *codec)
{
huff_codec_data* huff_codec = (huff_codec_data*) codec;
delete_huffman_decoder(huff_codec->decoder);
}
chd_error huff_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
huff_codec_data* huff_codec = (huff_codec_data*) codec;
struct bitstream* bitbuf = create_bitstream(src, complen);
uint32_t cur;
chd_error result;
/* first import the tree */
enum huffman_error err = huffman_import_tree_huffman(huff_codec->decoder, bitbuf);
if (err != HUFFERR_NONE)
{
free(bitbuf);
return CHDERR_DECOMPRESSION_ERROR;
}
/* then decode the data */
for (cur = 0; cur < destlen; cur++)
dest[cur] = huffman_decode_one(huff_codec->decoder, bitbuf);
bitstream_flush(bitbuf);
result = bitstream_overflow(bitbuf) ? CHDERR_DECOMPRESSION_ERROR : CHDERR_NONE;
free(bitbuf);
return result;
}
+266
View File
@@ -0,0 +1,266 @@
#include "codec_lzma.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/***************************************************************************
* LZMA ALLOCATOR HELPER
***************************************************************************
*/
static void *lzma_fast_alloc(ISzAllocPtr p, size_t size);
static void lzma_fast_free(ISzAllocPtr p, void *address);
/*-------------------------------------------------
* lzma_allocator_init
*-------------------------------------------------
*/
static void lzma_allocator_init(void* p)
{
lzma_allocator *codec = (lzma_allocator *)(p);
/* reset pointer list */
memset(codec->allocptr, 0, sizeof(codec->allocptr));
memset(codec->allocptr2, 0, sizeof(codec->allocptr2));
codec->base.Alloc = lzma_fast_alloc;
codec->base.Free = lzma_fast_free;
}
/*-------------------------------------------------
* lzma_allocator_free
*-------------------------------------------------
*/
static void lzma_allocator_free(void* p )
{
int i;
lzma_allocator *codec = (lzma_allocator *)(p);
/* free our memory */
for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++)
{
if (codec->allocptr[i] != NULL)
free(codec->allocptr[i]);
}
}
/*-------------------------------------------------
* lzma_fast_alloc - fast malloc for lzma, which
* allocates and frees memory frequently
*-------------------------------------------------
*/
/* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */
#define LZMA_MIN_ALIGNMENT_BITS 512
#define LZMA_MIN_ALIGNMENT_BYTES (LZMA_MIN_ALIGNMENT_BITS / 8)
static void *lzma_fast_alloc(ISzAllocPtr p, size_t size)
{
int scan;
uint32_t *addr = NULL;
lzma_allocator *codec = (lzma_allocator *)(p);
uintptr_t vaddr = 0;
/* compute the size, rounding to the nearest 1k */
size = (size + 0x3ff) & ~0x3ff;
/* reuse a hunk if we can */
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
{
uint32_t *ptr = codec->allocptr[scan];
if (ptr != NULL && size == *ptr)
{
/* set the low bit of the size so we don't match next time */
*ptr |= 1;
/* return aligned address of the block */
return codec->allocptr2[scan];
}
}
/* alloc a new one and put it into the list */
addr = (uint32_t *)malloc(size + sizeof(uint32_t) + LZMA_MIN_ALIGNMENT_BYTES);
if (addr==NULL)
return NULL;
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
{
if (codec->allocptr[scan] == NULL)
{
/* store block address */
codec->allocptr[scan] = addr;
/* compute aligned address, store it */
vaddr = (uintptr_t)addr;
vaddr = (vaddr + sizeof(uint32_t) + (LZMA_MIN_ALIGNMENT_BYTES-1)) & (~(LZMA_MIN_ALIGNMENT_BYTES-1));
codec->allocptr2[scan] = (uint32_t*)vaddr;
break;
}
}
/* set the low bit of the size so we don't match next time */
*addr = size | 1;
/* return aligned address */
return (void*)vaddr;
}
/*-------------------------------------------------
* lzma_fast_free - fast free for lzma, which
* allocates and frees memory frequently
*-------------------------------------------------
*/
static void lzma_fast_free(ISzAllocPtr p, void *address)
{
int scan;
uint32_t *ptr = NULL;
lzma_allocator *codec = NULL;
if (address == NULL)
return;
codec = (lzma_allocator *)(p);
/* find the hunk */
ptr = (uint32_t *)address;
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
{
if (ptr == codec->allocptr2[scan])
{
/* clear the low bit of the size to allow matches */
*codec->allocptr[scan] &= ~1;
return;
}
}
}
/***************************************************************************
* LZMA DECOMPRESSOR
***************************************************************************
*/
/*-------------------------------------------------
* lzma_compute_aligned_dictionary_size
* Based on LzmaEncProps_Normalize, LzmaEnc_SetProps, LzmaEnc_WriteProperties.
*-------------------------------------------------
*/
static uint32_t lzma_compute_aligned_dictionary_size(uint32_t hunkbytes)
{
const unsigned int level = 9;
const uint32_t reduceSize = hunkbytes;
uint32_t dictSize, alignedDictSize;
/* LzmaEncProps_Normalize */
dictSize = level <= 4 ?
(uint32_t)1 << (level * 2 + 16) :
level <= sizeof(size_t) / 2 + 4 ?
(uint32_t)1 << (level + 20) :
(uint32_t)1 << (sizeof(size_t) / 2 + 24);
if (dictSize > reduceSize)
{
const uint32_t kReduceMin = (uint32_t)1 << 12;
const uint32_t max = MIN(kReduceMin, reduceSize);
dictSize = MAX(max, dictSize);
}
/* LzmaEnc_SetProps */
dictSize = MIN((uint32_t)15 << 28, dictSize); /* kLzmaMaxHistorySize */
/* LzmaEnc_WriteProperties */
/* we write aligned dictionary value to properties for lzma decoder */
if (dictSize >= ((uint32_t)1 << 21))
{
const uint32_t kDictMask = ((uint32_t)1 << 20) - 1;
alignedDictSize = (dictSize + kDictMask) & ~kDictMask;
alignedDictSize = MIN(dictSize, alignedDictSize);
}
else
{
unsigned int i = 11 * 2;
do
{
alignedDictSize = (uint32_t)(2 + (i & 1)) << (i >> 1);
i++;
}
while (alignedDictSize < dictSize);
}
return alignedDictSize;
}
/*-------------------------------------------------
* lzma_codec_init - constructor
*-------------------------------------------------
*/
chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)
{
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
lzma_allocator* alloc = &lzma_codec->allocator;
const uint32_t alignedDictSize = lzma_compute_aligned_dictionary_size(hunkbytes);
unsigned int i;
Byte decoder_props[LZMA_PROPS_SIZE];
decoder_props[0] = 93;
for (i = 0; i < LZMA_PROPS_SIZE - 1; ++i)
decoder_props[1 + i] = (alignedDictSize >> (8 * i)) & 0xFF;
lzma_allocator_init(alloc);
/* construct the decoder */
LzmaDec_Construct(&lzma_codec->decoder);
/* do memory allocations */
if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, &alloc->base) != SZ_OK)
return CHDERR_DECOMPRESSION_ERROR;
/* Okay */
return CHDERR_NONE;
}
/*-------------------------------------------------
* lzma_codec_free
*-------------------------------------------------
*/
void lzma_codec_free(void* codec)
{
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
/* free memory */
LzmaDec_Free(&lzma_codec->decoder, &lzma_codec->allocator.base);
lzma_allocator_free(&lzma_codec->allocator);
}
/*-------------------------------------------------
* decompress - decompress data using the LZMA
* codec
*-------------------------------------------------
*/
chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
ELzmaStatus status;
SRes res;
SizeT consumedlen, decodedlen;
/* initialize */
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
LzmaDec_Init(&lzma_codec->decoder);
/* decode */
consumedlen = complen;
decodedlen = destlen;
res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);
if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen)
return CHDERR_DECOMPRESSION_ERROR;
return CHDERR_NONE;
}
+180
View File
@@ -0,0 +1,180 @@
#include "codec_zlib.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
static voidpf zlib_fast_alloc(voidpf opaque, zlib_alloc_size items, zlib_alloc_size size);
static void zlib_fast_free(voidpf opaque, voidpf address);
static void zlib_allocator_free(voidpf opaque);
/*-------------------------------------------------
zlib_codec_init - initialize the ZLIB codec
-------------------------------------------------*/
chd_error zlib_codec_init(void *codec, uint32_t hunkbytes)
{
int zerr;
chd_error err;
zlib_codec_data *data = (zlib_codec_data*)codec;
(void)hunkbytes;
/* clear the buffers */
memset(data, 0, sizeof(zlib_codec_data));
/* init the inflater first */
data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */
data->inflater.avail_in = 0;
data->inflater.zalloc = zlib_fast_alloc;
data->inflater.zfree = zlib_fast_free;
data->inflater.opaque = &data->allocator;
zerr = inflateInit2(&data->inflater, -MAX_WBITS);
/* convert errors */
if (zerr == Z_MEM_ERROR)
err = CHDERR_OUT_OF_MEMORY;
else if (zerr != Z_OK)
err = CHDERR_CODEC_ERROR;
else
err = CHDERR_NONE;
return err;
}
/*-------------------------------------------------
zlib_codec_free - free data for the ZLIB
codec
-------------------------------------------------*/
void zlib_codec_free(void *codec)
{
zlib_codec_data *data = (zlib_codec_data *)codec;
/* deinit the streams */
if (data != NULL)
{
inflateEnd(&data->inflater);
/* free our fast memory */
zlib_allocator_free(&data->allocator);
}
}
/*-------------------------------------------------
zlib_codec_decompress - decompress data using
the ZLIB codec
-------------------------------------------------*/
chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
zlib_codec_data *data = (zlib_codec_data *)codec;
int zerr;
/* reset the decompressor */
data->inflater.next_in = (Bytef *)src;
data->inflater.avail_in = complen;
data->inflater.total_in = 0;
data->inflater.next_out = (Bytef *)dest;
data->inflater.avail_out = destlen;
data->inflater.total_out = 0;
zerr = inflateReset(&data->inflater);
if (zerr != Z_OK)
return CHDERR_DECOMPRESSION_ERROR;
/* do it */
zerr = inflate(&data->inflater, Z_FINISH);
if (data->inflater.total_out != destlen)
return CHDERR_DECOMPRESSION_ERROR;
return CHDERR_NONE;
}
/*-------------------------------------------------
zlib_fast_alloc - fast malloc for ZLIB, which
allocates and frees memory frequently
-------------------------------------------------*/
/* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */
#define ZLIB_MIN_ALIGNMENT_BITS 512
#define ZLIB_MIN_ALIGNMENT_BYTES (ZLIB_MIN_ALIGNMENT_BITS / 8)
static voidpf zlib_fast_alloc(voidpf opaque, zlib_alloc_size items, zlib_alloc_size size)
{
zlib_allocator *alloc = (zlib_allocator *)opaque;
uintptr_t paddr = 0;
uint32_t *ptr;
int i;
/* compute the size, rounding to the nearest 1k */
size = (size * items + 0x3ff) & ~0x3ff;
/* reuse a hunk if we can */
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
{
ptr = alloc->allocptr[i];
if (ptr && size == *ptr)
{
/* set the low bit of the size so we don't match next time */
*ptr |= 1;
/* return aligned block address */
return (voidpf)(alloc->allocptr2[i]);
}
}
/* alloc a new one */
ptr = (uint32_t *)malloc(size + sizeof(uint32_t) + ZLIB_MIN_ALIGNMENT_BYTES);
if (!ptr)
return NULL;
/* put it into the list */
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
if (!alloc->allocptr[i])
{
alloc->allocptr[i] = ptr;
paddr = (((uintptr_t)ptr) + sizeof(uint32_t) + (ZLIB_MIN_ALIGNMENT_BYTES-1)) & (~(ZLIB_MIN_ALIGNMENT_BYTES-1));
alloc->allocptr2[i] = (uint32_t*)paddr;
break;
}
/* set the low bit of the size so we don't match next time */
*ptr = size | 1;
/* return aligned block address */
return (voidpf)paddr;
}
/*-------------------------------------------------
zlib_fast_free - fast free for ZLIB, which
allocates and frees memory frequently
-------------------------------------------------*/
static void zlib_fast_free(voidpf opaque, voidpf address)
{
zlib_allocator *alloc = (zlib_allocator *)opaque;
uint32_t *ptr = (uint32_t *)address;
int i;
/* find the hunk */
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
if (ptr == alloc->allocptr2[i])
{
/* clear the low bit of the size to allow matches */
*(alloc->allocptr[i]) &= ~1;
return;
}
}
/*-------------------------------------------------
zlib_allocator_free
-------------------------------------------------*/
static void zlib_allocator_free(voidpf opaque)
{
zlib_allocator *alloc = (zlib_allocator *)opaque;
int i;
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
if (alloc->allocptr[i])
free(alloc->allocptr[i]);
}
+91
View File
@@ -0,0 +1,91 @@
#include "codec_zstd.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/*-------------------------------------------------
* zstd_codec_init - constructor
*-------------------------------------------------
*/
chd_error zstd_codec_init(void* codec, uint32_t hunkbytes)
{
zstd_codec_data* zstd_codec = (zstd_codec_data*) codec;
(void)hunkbytes;
zstd_codec->dstream = ZSTD_createDStream();
if (!zstd_codec->dstream) {
#if 0
printf("NO DSTREAM CREATED!\n");
#endif
return CHDERR_DECOMPRESSION_ERROR;
}
return CHDERR_NONE;
}
/*-------------------------------------------------
* zstd_codec_free
*-------------------------------------------------
*/
void zstd_codec_free(void* codec)
{
zstd_codec_data* zstd_codec = (zstd_codec_data*) codec;
ZSTD_freeDStream(zstd_codec->dstream);
}
/*-------------------------------------------------
* decompress - decompress data using the ZSTD
* codec
*-------------------------------------------------
*/
chd_error zstd_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
ZSTD_inBuffer input;
ZSTD_outBuffer output;
/* initialize */
zstd_codec_data* zstd_codec = (zstd_codec_data*) codec;
/* reset decompressor */
size_t zstd_res = ZSTD_initDStream(zstd_codec->dstream);
if (ZSTD_isError(zstd_res))
{
#if 0
printf("INITI DSTREAM FAILED!\n");
#endif
return CHDERR_DECOMPRESSION_ERROR;
}
input.src = src;
input.size = complen;
input.pos = 0;
output.dst = dest;
output.size = destlen;
output.pos = 0;
while ((input.pos < input.size) && (output.pos < output.size))
{
zstd_res = ZSTD_decompressStream(zstd_codec->dstream, &output, &input);
if (ZSTD_isError(zstd_res))
{
#if 0
printf("DECOMPRESSION ERROR IN LOOP\n");
#endif
return CHDERR_DECOMPRESSION_ERROR;
}
}
if (output.pos != output.size)
{
#if 0
printf("OUTPUT DOESN'T MATCH!\n");
#endif
return CHDERR_DECOMPRESSION_ERROR;
}
return CHDERR_NONE;
}
+329
View File
@@ -0,0 +1,329 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
***************************************************************************
flac.c
FLAC compression wrappers
***************************************************************************/
#include <string.h>
#include "../include/libchdr/flac.h"
#include "../include/libchdr/macros.h"
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO
#include "../include/dr_libs/dr_flac.h"
/***************************************************************************
* FLAC DECODER
***************************************************************************
*/
static size_t flac_decoder_read_callback(void *userdata, void *buffer, size_t bytes);
static drflac_bool32 flac_decoder_seek_callback(void *userdata, int offset, drflac_seek_origin origin);
static drflac_bool32 flac_decoder_tell_callback(void *userdata, drflac_int64 *cursor);
static void flac_decoder_metadata_callback(void *userdata, drflac_metadata *metadata);
static void flac_decoder_write_callback(void *userdata, void *buffer, size_t bytes);
/* getters (valid after reset) */
static uint32_t sample_rate(flac_decoder *decoder) { return decoder->sample_rate; }
static uint8_t channels(flac_decoder *decoder) { return decoder->channels; }
static uint8_t bits_per_sample(flac_decoder *decoder) { return decoder->bits_per_sample; }
/*-------------------------------------------------
* flac_decoder - constructor
*-------------------------------------------------
*/
int flac_decoder_init(flac_decoder *decoder)
{
decoder->decoder = NULL;
decoder->sample_rate = 0;
decoder->channels = 0;
decoder->bits_per_sample = 0;
decoder->compressed_offset = 0;
decoder->compressed_start = NULL;
decoder->compressed_length = 0;
decoder->compressed2_start = NULL;
decoder->compressed2_length = 0;
decoder->uncompressed_offset = 0;
decoder->uncompressed_length = 0;
decoder->uncompressed_swap = 0;
return 0;
}
/*-------------------------------------------------
* flac_decoder - destructor
*-------------------------------------------------
*/
void flac_decoder_free(flac_decoder* decoder)
{
if ((decoder != NULL) && (decoder->decoder != NULL)) {
drflac_close((drflac*)decoder->decoder);
decoder->decoder = NULL;
}
}
/*-------------------------------------------------
* reset - reset state with the original
* parameters
*-------------------------------------------------
*/
static int flac_decoder_internal_reset(flac_decoder* decoder)
{
decoder->compressed_offset = 0;
flac_decoder_free(decoder);
decoder->decoder = drflac_open_with_metadata(
flac_decoder_read_callback, flac_decoder_seek_callback,
flac_decoder_tell_callback, flac_decoder_metadata_callback,
decoder, NULL);
return (decoder->decoder != NULL);
}
/*-------------------------------------------------
* reset - reset state with new memory parameters
* and a custom-generated header
*-------------------------------------------------
*/
int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length)
{
/* modify the template header with our parameters */
static const uint8_t s_header_template[0x2a] =
{
0x66, 0x4C, 0x61, 0x43, /* +00: 'fLaC' stream header */
0x80, /* +04: metadata block type 0 (STREAMINFO), */
/* flagged as last block */
0x00, 0x00, 0x22, /* +05: metadata block length = 0x22 */
0x00, 0x00, /* +08: minimum block size */
0x00, 0x00, /* +0A: maximum block size */
0x00, 0x00, 0x00, /* +0C: minimum frame size (0 == unknown) */
0x00, 0x00, 0x00, /* +0F: maximum frame size (0 == unknown) */
0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, /* +12: sample rate (0x0ac44 == 44100), */
/* numchannels (2), sample bits (16), */
/* samples in stream (0 == unknown) */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* +1A: MD5 signature (0 == none) */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* +2A: start of stream data */
};
memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template));
decoder->custom_header[0x08] = decoder->custom_header[0x0a] = (block_size*num_channels) >> 8;
decoder->custom_header[0x09] = decoder->custom_header[0x0b] = (block_size*num_channels) & 0xff;
decoder->custom_header[0x12] = sample_rate >> 12;
decoder->custom_header[0x13] = sample_rate >> 4;
decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
/* configure the header ahead of the provided buffer */
decoder->compressed_start = (const uint8_t *)(decoder->custom_header);
decoder->compressed_length = sizeof(decoder->custom_header);
decoder->compressed2_start = (const uint8_t *)(buffer);
decoder->compressed2_length = length;
return flac_decoder_internal_reset(decoder);
}
/*-------------------------------------------------
* decode_interleaved - decode to an interleaved
* sound stream
*-------------------------------------------------
*/
int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_frames, int swap_endian)
{
int16_t buffer[2352 / sizeof(int16_t)]; /* 2352 is the number of bytes per CD audio sector */
uint32_t buf_frames = ARRAY_LENGTH(buffer) / channels(decoder);
/* configure the uncompressed buffer */
memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start));
decoder->uncompressed_start[0] = samples;
decoder->uncompressed_offset = 0;
decoder->uncompressed_length = num_frames;
decoder->uncompressed_swap = swap_endian;
/* loop until we get everything we want */
while (decoder->uncompressed_offset < decoder->uncompressed_length) {
uint32_t frames_to_do = MIN(num_frames, buf_frames);
if (!drflac_read_pcm_frames_s16((drflac*)decoder->decoder, frames_to_do, buffer))
return 0;
flac_decoder_write_callback(decoder, buffer, frames_to_do*sizeof(*buffer)*channels(decoder));
num_frames -= frames_to_do;
}
return 1;
}
/*-------------------------------------------------
* finish - finish up the decode
*-------------------------------------------------
*/
uint32_t flac_decoder_finish(flac_decoder* decoder)
{
/* get the final decoding position and move forward */
drflac *flac = (drflac*)decoder->decoder;
uint64_t position = decoder->compressed_offset;
/* ugh... there's no function to obtain bytes used in drflac :-/ */
position -= DRFLAC_CACHE_L2_LINES_REMAINING(&flac->bs) * sizeof(drflac_cache_t);
position -= DRFLAC_CACHE_L1_BITS_REMAINING(&flac->bs) / 8;
position -= flac->bs.unalignedByteCount;
/* adjust position if we provided the header */
if (position == 0)
return 0;
if (decoder->compressed_start == (const uint8_t *)(decoder->custom_header))
position -= decoder->compressed_length;
flac_decoder_free(decoder);
return position;
}
/*-------------------------------------------------
* detect_native_endian - detect system endianness
*-------------------------------------------------
*/
int flac_decoder_detect_native_endian(void)
{
uint16_t native_endian = 0;
*(uint8_t *)(&native_endian) = 1;
return (native_endian & 1);
}
/*-------------------------------------------------
* read_callback - handle reads from the input
* stream
*-------------------------------------------------
*/
static size_t flac_decoder_read_callback(void *userdata, void *buffer, size_t bytes)
{
flac_decoder *decoder = (flac_decoder*)userdata;
uint8_t *dst = (uint8_t*)buffer;
/* copy from primary buffer first */
uint32_t outputpos = 0;
if (outputpos < bytes && decoder->compressed_offset < decoder->compressed_length)
{
uint32_t bytes_to_copy = MIN(bytes - outputpos, decoder->compressed_length - decoder->compressed_offset);
memcpy(&dst[outputpos], decoder->compressed_start + decoder->compressed_offset, bytes_to_copy);
outputpos += bytes_to_copy;
decoder->compressed_offset += bytes_to_copy;
}
/* once we're out of that, copy from the secondary buffer */
if (outputpos < bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length)
{
uint32_t bytes_to_copy = MIN(bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length));
memcpy(&dst[outputpos], decoder->compressed2_start + decoder->compressed_offset - decoder->compressed_length, bytes_to_copy);
outputpos += bytes_to_copy;
decoder->compressed_offset += bytes_to_copy;
}
return outputpos;
}
/*-------------------------------------------------
* metadata_callback - handle STREAMINFO metadata
*-------------------------------------------------
*/
static void flac_decoder_metadata_callback(void *userdata, drflac_metadata *metadata)
{
flac_decoder *decoder = (flac_decoder*)userdata;
/* ignore all but STREAMINFO metadata */
if (metadata->type != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO)
return;
/* parse out the data we care about */
decoder->sample_rate = metadata->data.streaminfo.sampleRate;
decoder->bits_per_sample = metadata->data.streaminfo.bitsPerSample;
decoder->channels = metadata->data.streaminfo.channels;
}
/*-------------------------------------------------
* write_callback - handle writes to the output
* stream
*-------------------------------------------------
*/
static void flac_decoder_write_callback(void *userdata, void *buffer, size_t bytes)
{
int sampnum, chan;
int shift, blocksize;
flac_decoder * decoder = (flac_decoder *)userdata;
int16_t *sampbuf = (int16_t *)buffer;
int sampch = channels(decoder);
uint32_t offset = decoder->uncompressed_offset;
uint16_t usample;
/* interleaved case */
shift = decoder->uncompressed_swap ? 8 : 0;
blocksize = bytes / (sampch * sizeof(sampbuf[0]));
if (decoder->uncompressed_start[1] == NULL)
{
int16_t *dest = decoder->uncompressed_start[0] + offset * sampch;
for (sampnum = 0; sampnum < blocksize && offset < decoder->uncompressed_length; sampnum++, offset++)
for (chan = 0; chan < sampch; chan++) {
usample = (uint16_t)*sampbuf++;
*dest++ = (int16_t)((usample << shift) | (usample >> shift));
}
}
/* non-interleaved case */
else
{
for (sampnum = 0; sampnum < blocksize && offset < decoder->uncompressed_length; sampnum++, offset++)
for (chan = 0; chan < sampch; chan++) {
usample = (uint16_t)*sampbuf++;
if (decoder->uncompressed_start[chan] != NULL)
decoder->uncompressed_start[chan][offset] = (int16_t) ((usample << shift) | (usample >> shift));
}
}
decoder->uncompressed_offset = offset;
}
/*-------------------------------------------------
* seek_callback - handle seeks on the output
* stream
*-------------------------------------------------
*/
static drflac_bool32 flac_decoder_seek_callback(void *userdata, int offset, drflac_seek_origin origin)
{
flac_decoder * decoder = (flac_decoder *)userdata;
uint32_t length = decoder->compressed_length + decoder->compressed2_length;
if (origin == DRFLAC_SEEK_SET) {
uint32_t pos = offset;
if (pos <= length) {
decoder->compressed_offset = pos;
return DRFLAC_TRUE;
}
} else if (origin == DRFLAC_SEEK_CUR) {
uint32_t pos = decoder->compressed_offset + offset;
if (pos <= length) {
decoder->compressed_offset = pos;
return DRFLAC_TRUE;
}
}
return DRFLAC_FALSE;
}
/*-------------------------------------------------
* tell_callback - handle seeks on the output
* stream
*-------------------------------------------------
*/
static drflac_bool32 flac_decoder_tell_callback(void *userdata, drflac_int64 *cursor)
{
flac_decoder * decoder = (flac_decoder *)userdata;
*cursor = decoder->compressed_offset;
return 1;
}
+569
View File
@@ -0,0 +1,569 @@
/* license:BSD-3-Clause
* copyright-holders:Aaron Giles
****************************************************************************
huffman.c
Static Huffman compression and decompression helpers.
****************************************************************************
Maximum codelength is officially (alphabetsize - 1). This would be 255 bits
(since we use 1 byte values). However, it is also dependent upon the number
of samples used, as follows:
2 bits -> 3..4 samples
3 bits -> 5..7 samples
4 bits -> 8..12 samples
5 bits -> 13..20 samples
6 bits -> 21..33 samples
7 bits -> 34..54 samples
8 bits -> 55..88 samples
9 bits -> 89..143 samples
10 bits -> 144..232 samples
11 bits -> 233..376 samples
12 bits -> 377..609 samples
13 bits -> 610..986 samples
14 bits -> 987..1596 samples
15 bits -> 1597..2583 samples
16 bits -> 2584..4180 samples -> note that a 4k data size guarantees codelength <= 16 bits
17 bits -> 4181..6764 samples
18 bits -> 6765..10945 samples
19 bits -> 10946..17710 samples
20 bits -> 17711..28656 samples
21 bits -> 28657..46367 samples
22 bits -> 46368..75024 samples
23 bits -> 75025..121392 samples
24 bits -> 121393..196417 samples
25 bits -> 196418..317810 samples
26 bits -> 317811..514228 samples
27 bits -> 514229..832039 samples
28 bits -> 832040..1346268 samples
29 bits -> 1346269..2178308 samples
30 bits -> 2178309..3524577 samples
31 bits -> 3524578..5702886 samples
32 bits -> 5702887..9227464 samples
Looking at it differently, here is where powers of 2 fall into these buckets:
256 samples -> 11 bits max
512 samples -> 12 bits max
1k samples -> 14 bits max
2k samples -> 15 bits max
4k samples -> 16 bits max
8k samples -> 18 bits max
16k samples -> 19 bits max
32k samples -> 21 bits max
64k samples -> 22 bits max
128k samples -> 24 bits max
256k samples -> 25 bits max
512k samples -> 27 bits max
1M samples -> 28 bits max
2M samples -> 29 bits max
4M samples -> 31 bits max
8M samples -> 32 bits max
****************************************************************************
Delta-RLE encoding works as follows:
Starting value is assumed to be 0. All data is encoded as a delta
from the previous value, such that final[i] = final[i - 1] + delta.
Long runs of 0s are RLE-encoded as follows:
0x100 = repeat count of 8
0x101 = repeat count of 9
0x102 = repeat count of 10
0x103 = repeat count of 11
0x104 = repeat count of 12
0x105 = repeat count of 13
0x106 = repeat count of 14
0x107 = repeat count of 15
0x108 = repeat count of 16
0x109 = repeat count of 32
0x10a = repeat count of 64
0x10b = repeat count of 128
0x10c = repeat count of 256
0x10d = repeat count of 512
0x10e = repeat count of 1024
0x10f = repeat count of 2048
Note that repeat counts are reset at the end of a row, so if a 0 run
extends to the end of a row, a large repeat count may be used.
The reason for starting the run counts at 8 is that 0 is expected to
be the most common symbol, and is typically encoded in 1 or 2 bits.
***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../include/libchdr/huffman.h"
#include "../include/libchdr/macros.h"
/***************************************************************************
* MACROS
***************************************************************************
*/
#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f))
/***************************************************************************
* IMPLEMENTATION
***************************************************************************
*/
/*-------------------------------------------------
* huffman_context_base - create an encoding/
* decoding context
*-------------------------------------------------
*/
struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits)
{
struct huffman_decoder* decoder = NULL;
/* limit to 24 bits */
if (maxbits > 24)
return NULL;
decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder));
decoder->numcodes = numcodes;
decoder->maxbits = maxbits;
decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits));
decoder->huffnode = (struct node_t*)malloc(sizeof(struct node_t) * numcodes);
decoder->datahisto = NULL;
decoder->prevdata = 0;
decoder->rleremaining = 0;
return decoder;
}
void delete_huffman_decoder(struct huffman_decoder* decoder)
{
if (decoder != NULL)
{
if (decoder->lookup != NULL)
free(decoder->lookup);
if (decoder->huffnode != NULL)
free(decoder->huffnode);
free(decoder);
}
}
/*-------------------------------------------------
* decode_one - decode a single code from the
* huffman stream
*-------------------------------------------------
*/
uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf)
{
/* peek ahead to get maxbits worth of data */
uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits);
/* look it up, then remove the actual number of bits for this code */
lookup_value lookup = decoder->lookup[bits];
bitstream_remove(bitbuf, lookup & 0x1f);
/* return the value */
return lookup >> 5;
}
/*-------------------------------------------------
* import_tree_rle - import an RLE-encoded
* huffman tree from a source data stream
*-------------------------------------------------
*/
enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf)
{
int numbits;
uint32_t curnode;
enum huffman_error error;
/* bits per entry depends on the maxbits */
if (decoder->maxbits >= 16)
numbits = 5;
else if (decoder->maxbits >= 8)
numbits = 4;
else
numbits = 3;
/* loop until we read all the nodes */
for (curnode = 0; curnode < decoder->numcodes; )
{
/* a non-one value is just raw */
int nodebits = bitstream_read(bitbuf, numbits);
if (nodebits != 1)
decoder->huffnode[curnode++].numbits = nodebits;
/* a one value is an escape code */
else
{
/* a double 1 is just a single 1 */
nodebits = bitstream_read(bitbuf, numbits);
if (nodebits == 1)
decoder->huffnode[curnode++].numbits = nodebits;
/* otherwise, we need one for value for the repeat count */
else
{
int repcount = bitstream_read(bitbuf, numbits) + 3;
if (repcount + curnode > decoder->numcodes)
return HUFFERR_INVALID_DATA;
while (repcount--)
decoder->huffnode[curnode++].numbits = nodebits;
}
}
}
/* make sure we ended up with the right number */
if (curnode != decoder->numcodes)
return HUFFERR_INVALID_DATA;
/* assign canonical codes for all nodes based on their code lengths */
error = huffman_assign_canonical_codes(decoder);
if (error != HUFFERR_NONE)
return error;
/* build the lookup table */
error = huffman_build_lookup_table(decoder);
if (error != HUFFERR_NONE)
return error;
/* determine final input length and report errors */
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
}
/*-------------------------------------------------
* import_tree_huffman - import a huffman-encoded
* huffman tree from a source data stream
*-------------------------------------------------
*/
enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf)
{
int start;
int last = 0;
int count = 0;
int index;
uint32_t curcode;
uint8_t rlefullbits = 0;
uint32_t temp;
enum huffman_error error;
/* start by parsing the lengths for the small tree */
struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6);
smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3);
start = bitstream_read(bitbuf, 3) + 1;
for (index = 1; index < 24; index++)
{
if (index < start || count == 7)
smallhuff->huffnode[index].numbits = 0;
else
{
count = bitstream_read(bitbuf, 3);
smallhuff->huffnode[index].numbits = (count == 7) ? 0 : count;
}
}
/* then regenerate the tree */
error = huffman_assign_canonical_codes(smallhuff);
if (error != HUFFERR_NONE)
{
delete_huffman_decoder(smallhuff);
return error;
}
error = huffman_build_lookup_table(smallhuff);
if (error != HUFFERR_NONE)
{
delete_huffman_decoder(smallhuff);
return error;
}
/* determine the maximum length of an RLE count */
temp = decoder->numcodes - 9;
while (temp != 0)
temp >>= 1, rlefullbits++;
/* now process the rest of the data */
for (curcode = 0; curcode < decoder->numcodes; )
{
int value = huffman_decode_one(smallhuff, bitbuf);
if (value != 0)
decoder->huffnode[curcode++].numbits = last = value - 1;
else
{
int count = bitstream_read(bitbuf, 3) + 2;
if (count == 7+2)
count += bitstream_read(bitbuf, rlefullbits);
for ( ; count != 0 && curcode < decoder->numcodes; count--)
decoder->huffnode[curcode++].numbits = last;
}
}
/* make sure we free the local huffman decoder */
delete_huffman_decoder(smallhuff);
/* make sure we ended up with the right number */
if (curcode != decoder->numcodes)
return HUFFERR_INVALID_DATA;
/* assign canonical codes for all nodes based on their code lengths */
error = huffman_assign_canonical_codes(decoder);
if (error != HUFFERR_NONE)
return error;
/* build the lookup table */
error = huffman_build_lookup_table(decoder);
if (error != HUFFERR_NONE)
return error;
/* determine final input length and report errors */
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
}
/*-------------------------------------------------
* compute_tree_from_histo - common backend for
* computing a tree based on the data histogram
*-------------------------------------------------
*/
enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder)
{
uint32_t i;
uint32_t lowerweight;
uint32_t upperweight;
/* compute the number of data items in the histogram */
uint32_t sdatacount = 0;
for (i = 0; i < decoder->numcodes; i++)
sdatacount += decoder->datahisto[i];
/* binary search to achieve the optimum encoding */
lowerweight = 0;
upperweight = sdatacount * 2;
while (1)
{
/* build a tree using the current weight */
uint32_t curweight = (upperweight + lowerweight) / 2;
int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight);
/* apply binary search here */
if (curmaxbits <= decoder->maxbits)
{
lowerweight = curweight;
/* early out if it worked with the raw weights, or if we're done searching */
if (curweight == sdatacount || (upperweight - lowerweight) <= 1)
break;
}
else
upperweight = curweight;
}
/* assign canonical codes for all nodes based on their code lengths */
return huffman_assign_canonical_codes(decoder);
}
/***************************************************************************
* INTERNAL FUNCTIONS
***************************************************************************
*/
/*-------------------------------------------------
* tree_node_compare - compare two tree nodes
* by weight
*-------------------------------------------------
*/
static int huffman_tree_node_compare(const void *item1, const void *item2)
{
const struct node_t *node1 = *(const struct node_t **)item1;
const struct node_t *node2 = *(const struct node_t **)item2;
if (node2->weight != node1->weight)
return node2->weight - node1->weight;
#if 0
if (node2->bits - node1->bits == 0)
fprintf(stderr, "identical node sort keys, should not happen!\n");
#endif
return (int)node1->bits - (int)node2->bits;
}
/*-------------------------------------------------
* build_tree - build a huffman tree based on the
* data distribution
*-------------------------------------------------
*/
int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight)
{
uint32_t curcode;
int nextalloc;
int listitems = 0;
int maxbits = 0;
/* make a list of all non-zero nodes */
struct node_t** list = (struct node_t**)malloc(sizeof(struct node_t*) * decoder->numcodes * 2);
memset(decoder->huffnode, 0, decoder->numcodes * sizeof(decoder->huffnode[0]));
for (curcode = 0; curcode < decoder->numcodes; curcode++)
if (decoder->datahisto[curcode] != 0)
{
list[listitems++] = &decoder->huffnode[curcode];
decoder->huffnode[curcode].count = decoder->datahisto[curcode];
decoder->huffnode[curcode].bits = curcode;
/* scale the weight by the current effective length, ensuring we don't go to 0 */
decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata);
if (decoder->huffnode[curcode].weight == 0)
decoder->huffnode[curcode].weight = 1;
}
#if 0
fprintf(stderr, "Pre-sort:\n");
for (int i = 0; i < listitems; i++) {
fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
}
#endif
/* sort the list by weight, largest weight first */
qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare);
#if 0
fprintf(stderr, "Post-sort:\n");
for (int i = 0; i < listitems; i++) {
fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
}
fprintf(stderr, "===================\n");
#endif
/* now build the tree */
nextalloc = decoder->numcodes;
while (listitems > 1)
{
int curitem;
/* remove lowest two items */
struct node_t* node1 = &(*list[--listitems]);
struct node_t* node0 = &(*list[--listitems]);
/* create new node */
struct node_t* newnode = &decoder->huffnode[nextalloc++];
newnode->parent = NULL;
node0->parent = node1->parent = newnode;
newnode->weight = node0->weight + node1->weight;
/* insert into list at appropriate location */
for (curitem = 0; curitem < listitems; curitem++)
if (newnode->weight > list[curitem]->weight)
{
memmove(&list[curitem+1], &list[curitem], (listitems - curitem) * sizeof(list[0]));
break;
}
list[curitem] = newnode;
listitems++;
}
/* compute the number of bits in each code, and fill in another histogram */
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
struct node_t *curnode;
struct node_t* node = &decoder->huffnode[curcode];
node->numbits = 0;
node->bits = 0;
/* if we have a non-zero weight, compute the number of bits */
if (node->weight > 0)
{
/* determine the number of bits for this node */
for (curnode = node; curnode->parent != NULL; curnode = curnode->parent)
node->numbits++;
if (node->numbits == 0)
node->numbits = 1;
/* keep track of the max */
maxbits = MAX(maxbits, ((int)node->numbits));
}
}
return maxbits;
}
/*-------------------------------------------------
* assign_canonical_codes - assign canonical codes
* to all the nodes based on the number of bits
* in each
*-------------------------------------------------
*/
enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder)
{
uint32_t curcode;
int codelen;
uint32_t curstart = 0;
/* build up a histogram of bit lengths */
uint32_t bithisto[33] = { 0 };
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > decoder->maxbits)
return HUFFERR_INTERNAL_INCONSISTENCY;
if (node->numbits <= 32)
bithisto[node->numbits]++;
}
/* for each code length, determine the starting code number */
for (codelen = 32; codelen > 0; codelen--)
{
uint32_t nextstart = (curstart + bithisto[codelen]) >> 1;
if (codelen != 1 && nextstart * 2 != (curstart + bithisto[codelen]))
return HUFFERR_INTERNAL_INCONSISTENCY;
bithisto[codelen] = curstart;
curstart = nextstart;
}
/* now assign canonical codes */
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > 0)
node->bits = bithisto[node->numbits]++;
}
return HUFFERR_NONE;
}
/*-------------------------------------------------
* build_lookup_table - build a lookup table for
* fast decoding
*-------------------------------------------------
*/
enum huffman_error huffman_build_lookup_table(struct huffman_decoder* decoder)
{
const lookup_value* lookupend = &decoder->lookup[(1u << decoder->maxbits)];
uint32_t curcode;
/* iterate over all codes */
for (curcode = 0; curcode < decoder->numcodes; curcode++)
{
/* process all nodes which have non-zero bits */
struct node_t* node = &decoder->huffnode[curcode];
if (node->numbits > 0)
{
int shift;
lookup_value *dest;
lookup_value *destend;
/* set up the entry */
lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
/* fill all matching entries */
shift = decoder->maxbits - node->numbits;
dest = &decoder->lookup[node->bits << shift];
destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
if (dest >= lookupend || destend >= lookupend || destend < dest)
return HUFFERR_INTERNAL_INCONSISTENCY;
while (dest <= destend)
*dest++ = value;
}
}
return HUFFERR_NONE;
}
+5
View File
@@ -0,0 +1,5 @@
{
global: chd_*;
local: *;
};
+36
View File
@@ -0,0 +1,36 @@
/* Disable unused features of miniz (but allow
them to be restored by dependent projects). */
#ifndef MINIZ_ARCHIVE_APIS
#define MINIZ_NO_ARCHIVE_APIS
#endif
#ifndef MINIZ_DEFLATE_APIS
#define MINIZ_NO_DEFLATE_APIS
#endif
#ifndef MINIZ_STDIO
#define MINIZ_NO_STDIO
#endif
#ifndef MINIZ_TIME
#define MINIZ_NO_TIME
#endif
#include "deps/lzma-25.01/src/LzmaDec.c"
#include "deps/miniz-3.1.1/miniz.c"
#include "deps/zstd-1.5.7/zstddeclib.c"
#include "src/libchdr_bitstream.c"
#include "src/libchdr_cdrom.c"
#include "src/libchdr_chd.c"
#include "src/libchdr_codec_cdfl.c"
#include "src/libchdr_codec_cdlz.c"
#include "src/libchdr_codec_cdzl.c"
#include "src/libchdr_codec_cdzs.c"
#include "src/libchdr_codec_flac.c"
#include "src/libchdr_codec_huff.c"
#include "src/libchdr_codec_lzma.c"
#include "src/libchdr_codec_zlib.c"
#include "src/libchdr_codec_zstd.c"
#include "src/libchdr_flac.c"
#include "src/libchdr_huffman.c"
+2 -2
View File
@@ -1,7 +1,7 @@
# Software Information
display_name = "Atari - Jaguar (Virtual Jaguar)"
authors = "Joseph Mattiello|David Raingeard|Shamus"
supported_extensions = "j64|jag|rom|abs|cof|bin|prg|cue|cdi"
supported_extensions = "j64|jag|rom|abs|cof|bin|prg|cue|cdi|chd"
corename = "Virtual Jaguar"
license = "GPLv3"
permissions = ""
@@ -39,4 +39,4 @@ firmware1_desc = "[BIOS] Atari Jaguar Developer CD (World).j64 (Jaguar CD BIOS,
firmware1_path = "[BIOS] Atari Jaguar Developer CD (World).j64"
firmware1_opt = "true"
description = "The actively maintained Atari Jaguar and Jaguar CD emulator for libretro, continuing the Virtual Jaguar project (originally by David Raingeard of Potato Emulation). No BIOS files are required: the Jaguar boot ROM and CD BIOS images are embedded in the core. Supports Jaguar CD (CUE/BIN, CDI) with Memory Track saves, JagLink/CatBox network play over TCP or RetroArch netplay, a high-level BIOS that lets most commercial titles boot without a real BIOS image, save states, SRAM, cheat codes, RetroAchievements, and M68K/RISC clock-scale (overclock) core options for framerate-limited games. The accurate blitter is SIMD-accelerated (SSE2 on x86, NEON on ARM); the Blitter core option can be set to 'Fast' to trade some accuracy for additional speed on lower-end hardware."
description = "The actively maintained Atari Jaguar and Jaguar CD emulator for libretro, continuing the Virtual Jaguar project (originally by David Raingeard of Potato Emulation). No BIOS files are required: the Jaguar boot ROM and CD BIOS images are embedded in the core. Supports Jaguar CD (CUE/BIN, CDI, CHD) with Memory Track saves, JagLink/CatBox network play over TCP or RetroArch netplay, a high-level BIOS that lets most commercial titles boot without a real BIOS image, save states, SRAM, cheat codes, RetroAchievements, and M68K/RISC clock-scale (overclock) core options for framerate-limited games. CHD images must be created with a chdman that writes CHSE session metadata (MAME 2026-08-15 / PR 15886 or later); see the core's jagcd tools. The accurate blitter is SIMD-accelerated (SSE2 on x86, NEON on ARM); the Blitter core option can be set to 'Fast' to trade some accuracy for additional speed on lower-end hardware."
+7 -1
View File
@@ -117,7 +117,13 @@ from structured noise at the right level.
which survive an ISO rip. No retail game can ever start from one
(BigPEmu declines ISOs for the same reason), so the loader refuses
`.iso` outright with an explanatory error instead of raising false
hopes with a BIOS screen that goes nowhere. Use CUE/BIN or CDI.
hopes with a BIOS screen that goes nowhere. Use CUE/BIN, CDI, or a
CHD created with a chdman that writes CHSE session tags (see
`docs/jagcd-chd.md`).
- **Old / internet CHD rips**: CHD is supported, but files made before
MAME PR 15886 (no `CHSE` session metadata) flattened Jaguar CD's two
sessions and are refused at load. Reconvert from CUE/BIN with
`tools/jagcd`. Virtual-pregap CHDs warn and still load (HLE is fine).
- **CD read speeds above 2x on the real-BIOS path**: the
`virtualjaguar_cd_read_speed` option is HLE-only by design; scaling the
BIOS path's FIFO/DSA cadence re-opens the DSA-steal and FIFO-storm race
+40
View File
@@ -0,0 +1,40 @@
# Jaguar CD CHD images
CHD is supported for Jaguar CD **only** when the file carries MAME's `CHSE` session-metadata tag ([mamedev/mame#15886](https://github.com/mamedev/mame/pull/15886), merged 2026-08-15). That tag is the CHD equivalent of a CUE `REM SESSION nn` line.
## Why most `.chd` files on the internet will not load
Jaguar CDs are two-session discs: session 1 is Red Book audio, session 2 is game data mastered as audio-type 2352-byte tracks. Classic `chdman createcd` flattened that into one session and dropped the ~11400-sector inter-session gap, so the TOC the BIOS and HLE walk is wrong.
Those files are **refused** at load with a `[CD-CHD]` error pointing here. Reconvert from a Redump CUE/BIN (or from CDI, after turning it into CUE/BIN). Do not try to "fix" an old CHD in place.
`chdman` 0.288 from Homebrew, and every CHD made before PR 15886, is in this class. `chdman info` on a good file shows a `CHSE` / `SESSION:2` metadata blob; a bad file has only `CHT2` track lines.
## Convert
1. Get the `jagcd-tools-{linux-x64,macos,windows-x64}.zip` from this repository's GitHub Releases (tagged `v*` and the rolling `nightly`). Each zip contains a `chdman` built from the commit in [`tools/jagcd/CHDMAN_PIN`](../tools/jagcd/CHDMAN_PIN) plus `jagcd-to-chd` and `jagcd-chd-check`. They ship next to the cores from `release.yml`.
2. From a CUE:
```
./jagcd-to-chd "Game (USA).cue"
```
That runs `chdman createcd` and then `jagcd-chd-check`. Exit 0 is loadable. Exit 1 means the `chdman` you invoked is still too old — set `JAGCD_CHDMAN` to the binary from the zip, not the one on `PATH`.
`createcd` does not read `.cdi`. Use CUE/BIN as the source, or load the CDI directly in the core.
## Virtual pregaps (exit 2 / log warning)
`CHT2` `PGTYPE` values that start with `V` (e.g. `VAUDIO`) mean the pregap is **silence**, not stored audio. That is the same situation as a Redump CUE/BIN: HLE boot works (auth is bypassed), real-BIOS authentication may fail. The core **warns and loads**. CDI-class dumps that preserve INDEX 00 audio are the right source if you need BIOS-mode auth.
PR 15886 did not change pregap-byte encoding; it only added session tags.
## Core behaviour
- `.chd` is a path-loaded disc image, same as `.cue` / `.cdi`.
- Missing `CHSE` on a Jaguar-shaped image (2+ all-audio tracks) → refuse.
- `CHSE` present, virtual pregaps → warn, load.
- CHD audio frames are stored as 16-bit PCM. The core byte-swaps them back to Jaguar I2S (CUE/BIN) order on read.
- The ~11400-sector session gap is synthesized when `CHSE` shows a session change and the first session-2 track's `PREGAP` is not already that large (so a CHD that encoded the gap as a pregap is not double-counted). Measured on a post-15886 `createcd` of Frog Feast: every `CHT2` `PREGAP` is 0 and `PGTYPE` is `MODE1` (not virtual); `extractcd` emits `REM SESSION 02`. Retail Redump CUEs have one audio track in session 1, so `CHSE` index 1 is session 2's first track. Homebrew dumps with two session-1 tracks may see MAME tag session 2 one track early — reconvert from a CUE the core already boots if a title looks wrong.
Tracking: issue #322.
+2 -2
View File
@@ -61,7 +61,7 @@ For each of the 14 matrix platforms (Linux x86_64/aarch64/i686, macOS arm64/x86_
3. Strip the shipped binary so it's small.
4. Upload as a CI artifact.
Two extra container-based jobs do PS Vita (`vitasdk/vitasdk:latest`) and Nintendo Switch (`devkitpro/devkita64:latest`).
Two extra container-based jobs do PS Vita (`vitasdk/vitasdk:latest`) and Nintendo Switch (`devkitpro/devkita64:latest`). A parallel `jagcd-tools` job (`.github/workflows/chdman-tools.yml`) builds pinned `chdman` plus `jagcd-to-chd` / `jagcd-chd-check` for linux-x64, macOS, and windows-x64 — the same desktop trio as `cue2cdi`.
After all platform builds finish, the `release` job:
@@ -74,7 +74,7 @@ After all platform builds finish, the `release` job:
### 3b. Nightly builds (develop pushes)
`release.yml` also fires on every push to `develop`. The build matrix is
identical — the same 16 jobs (14 matrix entries + Vita + Switch), the same split-debug packaging — but the
identical — the same 16 jobs (14 matrix entries + Vita + Switch) plus the jagcd-tools zips, the same split-debug packaging — but the
publishing step is the `nightly` job instead of `release`:
1. Moves the `nightly` git tag onto the pushed commit.
+2 -2
View File
@@ -47,7 +47,7 @@ _VERSION_GEN := $(shell sh $(CORE_DIR)/scripts/gen-version-h.sh && echo ok)
include $(CLEAR_VARS)
LOCAL_MODULE := retro
LOCAL_SRC_FILES := $(SOURCES_C)
LOCAL_CFLAGS := $(COREFLAGS)
LOCAL_SRC_FILES := $(SOURCES_C) $(SOURCES_LIBCHDR)
LOCAL_CFLAGS := $(COREFLAGS) $(LIBCHDR_CFLAGS)
LOCAL_LDFLAGS := -Wl,-version-script=$(CORE_DIR)/link.T
include $(BUILD_SHARED_LIBRARY)
+7 -4
View File
@@ -70,9 +70,11 @@ int64_t rfread(void* buffer, size_t elem_size, size_t elem_count, RFILE* stream)
* cof : COFF binaries (also routes through JST_ABS_TYPE1)
* bin, prg : conservative headerless raw-homebrew with valid
* 68k bootstrap (JST_RAW_BINARY)
* cue, cdi : Jaguar CD images (CUE/BIN and CDI). Bare `iso`
* images are not bootable -- see docs/cd-known-issues.md. */
#define JAGUAR_VALID_EXTENSIONS "j64|jag|rom|abs|cof|bin|prg|cue|cdi"
* cue, cdi, chd : Jaguar CD images (CUE/BIN, CDI, and CHD). Bare `iso`
* images are not bootable -- see docs/cd-known-issues.md.
* CHD files need session metadata from a post-2026-08
* chdman; see docs/jagcd-chd.md. */
#define JAGUAR_VALID_EXTENSIONS "j64|jag|rom|abs|cof|bin|prg|cue|cdi|chd"
/* Framebuffer allocation, in pixels. Sized for the widest / tallest video
* mode TOM can be programmed into (TOMWriteWord clamps tomWidth to 1024 and
@@ -648,7 +650,7 @@ void retro_set_environment(retro_environment_t cb)
{
static const struct retro_system_content_info_override
content_overrides[] = {
{ "cue|cdi", true /* need_fullpath */, false /* persistent_data */ },
{ "cue|cdi|chd", true /* need_fullpath */, false /* persistent_data */ },
{ NULL, false, false }
};
environ_cb(RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE,
@@ -2317,6 +2319,7 @@ bool retro_load_game(const struct retro_game_info *info)
is_cd_content = info->path && (has_extension(info->path, "cue")
|| has_extension(info->path, "cdi")
|| has_extension(info->path, "chd")
|| has_extension(info->path, "iso"));
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
+3 -1
View File
@@ -16,7 +16,7 @@ ROOT=$(cd "$(dirname "$0")/.." && pwd)
CC="${CC:-gcc}"
CFLAGS="-fsyntax-only -std=gnu89 -Werror=declaration-after-statement"
INCLUDES="-I. -Isrc -Isrc/core -Isrc/tom -Isrc/jerry -Isrc/cd -Isrc/bios -Isrc/m68000 -Ilibretro-common/include"
INCLUDES="-I. -Isrc -Isrc/core -Isrc/tom -Isrc/jerry -Isrc/cd -Isrc/bios -Isrc/m68000 -Ilibretro-common/include -Ideps/libchdr/include"
DEFINES='-D__LIBRETRO__ -DINLINE=inline'
skip_file() {
@@ -32,6 +32,8 @@ skip_file() {
test/tools/test_rcheevos_e2e.c) return 0 ;;
# Diagnostic tools — not part of the libretro core build.
test/tools/flicker_detect.c) return 0 ;;
deps/libchdr/*) return 0 ;;
tools/jagcd/*) return 0 ;;
# Builds against test/harness/, which is outside $INCLUDES; C99 harness.
test/tools/fmv_seek_probe.c) return 0 ;;
test/tools/frame_hash_ab.c) return 0 ;;
+502 -79
View File
@@ -23,10 +23,33 @@
#include "jaguar.h"
#include "log.h"
#include <libchdr/chd.h>
#include <libchdr/cdrom.h>
#ifndef CDROM_SESSION_METADATA_TAG
#define CDROM_SESSION_METADATA_TAG CHD_MAKE_TAG('C','H','S','E')
#endif
#ifndef CDROM_SESSION_METADATA_FORMAT
#define CDROM_SESSION_METADATA_FORMAT "SESSION:%d"
#endif
// CDI (DiscJuggler) format support
static RFILE *cdi_file = NULL;
static bool ParseCDI(const char *cdiPath);
/* CHD (MAME compressed hunks) — libchdr, opened through libretro VFS. */
static chd_file *chd = NULL;
static RFILE *chd_rfile = NULL;
static uint8_t *chd_hunk = NULL;
static uint32_t chd_hunkbytes = 0;
static int32_t chd_hunknum = -1;
/* First CHD frame of each track's stored data (includes 4-frame padding
* of earlier tracks; does NOT include virtual pregaps). */
static uint32_t chd_frame0[CDINTF_MAX_TRACKS];
static uint8_t chd_virtual_pregap[CDINTF_MAX_TRACKS];
static bool ParseCHD(const char *chdPath);
static void CDIntfCloseCHD(void);
/* Multi-file CUE sector reads used to rfopen/rfseek/rfread/rfclose the
* track's BIN on EVERY 2352-byte sector (measured 18.3 us vs 0.4 us per
* sector on APFS; the gap is syscall latency, so it is far worse on SD
@@ -69,6 +92,7 @@ static int cdintf_strncasecmp(const char *a, const char *b, size_t n)
// Private function prototypes
static bool ParseCueSheet(const char *cuePath);
static void CDIntfFinalizeSessions(void);
static void MSFFromLBA(uint32_t lba, uint8_t *m, uint8_t *s, uint8_t *f);
static uint32_t LBAFromMSF(uint8_t m, uint8_t s, uint8_t f);
static char *TrimWhitespace(char *str);
@@ -153,6 +177,436 @@ static bool GetDirectoryFromPath(const char *path, char *dir, size_t dirSize)
return false;
}
static void CDIntfFinalizeSessions(void)
{
int i;
uint32_t sess1Min = 99, sess1Max = 0;
uint32_t sess2Min = 99, sess2Max = 0;
disc.numSessions = 1;
for (i = 0; i < (int)disc.numTracks; i++)
{
uint32_t trackNum = disc.tracks[i].number;
uint32_t sess = disc.tracks[i].session;
if (sess == 1)
{
if (trackNum < sess1Min) sess1Min = trackNum;
if (trackNum > sess1Max) sess1Max = trackNum;
}
else if (sess == 2)
{
disc.numSessions = 2;
if (trackNum < sess2Min) sess2Min = trackNum;
if (trackNum > sess2Max) sess2Max = trackNum;
}
}
disc.sessions[0].number = 1;
disc.sessions[0].firstTrack = (sess1Min <= CDINTF_MAX_TRACKS) ? sess1Min : 1;
disc.sessions[0].lastTrack = (sess1Max > 0) ? sess1Max : 1;
if (disc.numSessions >= 2 && sess2Min <= CDINTF_MAX_TRACKS)
{
uint32_t leadOut = disc.tracks[sess2Min - 1].startLBA;
disc.sessions[0].leadOutLBA = leadOut;
MSFFromLBA(leadOut, &disc.sessions[0].leadOutM,
&disc.sessions[0].leadOutS, &disc.sessions[0].leadOutF);
}
else
{
uint32_t lastIdx = disc.sessions[0].lastTrack - 1;
uint32_t leadOut = disc.tracks[lastIdx].startLBA + disc.tracks[lastIdx].lengthLBA;
disc.sessions[0].leadOutLBA = leadOut;
MSFFromLBA(leadOut, &disc.sessions[0].leadOutM,
&disc.sessions[0].leadOutS, &disc.sessions[0].leadOutF);
}
if (disc.numSessions >= 2)
{
uint32_t lastIdx, leadOut;
disc.sessions[1].number = 2;
disc.sessions[1].firstTrack = sess2Min;
disc.sessions[1].lastTrack = sess2Max;
lastIdx = sess2Max - 1;
leadOut = disc.tracks[lastIdx].startLBA + disc.tracks[lastIdx].lengthLBA;
disc.sessions[1].leadOutLBA = leadOut;
MSFFromLBA(leadOut, &disc.sessions[1].leadOutM,
&disc.sessions[1].leadOutS, &disc.sessions[1].leadOutF);
}
}
static uint64_t chd_vfs_fsize(void *argp)
{
int64_t sz;
sz = filestream_get_size((RFILE *)argp);
if (sz < 0)
return (uint64_t)-1;
return (uint64_t)sz;
}
static size_t chd_vfs_fread(void *ptr, size_t size, size_t nmemb, void *argp)
{
int64_t want;
int64_t got;
if (size == 0 || nmemb == 0)
return 0;
want = (int64_t)size * (int64_t)nmemb;
got = filestream_read((RFILE *)argp, ptr, want);
if (got < 0)
return 0;
return (size_t)(got / (int64_t)size);
}
static int chd_vfs_fclose(void *argp)
{
/* We own the RFILE; chd_close must not fclose it (non-owner). */
(void)argp;
return 0;
}
static int chd_vfs_fseek(void *argp, int64_t offset, int whence)
{
int pos;
if (whence == SEEK_SET)
pos = RETRO_VFS_SEEK_POSITION_START;
else if (whence == SEEK_CUR)
pos = RETRO_VFS_SEEK_POSITION_CURRENT;
else if (whence == SEEK_END)
pos = RETRO_VFS_SEEK_POSITION_END;
else
return -1;
if (filestream_seek((RFILE *)argp, offset, pos) < 0)
return -1;
return 0;
}
static const core_file_callbacks chd_vfs_cb = {
chd_vfs_fsize,
chd_vfs_fread,
chd_vfs_fclose,
chd_vfs_fseek
};
static void CDIntfCloseCHD(void)
{
if (chd)
{
chd_close(chd);
chd = NULL;
}
if (chd_rfile)
{
rfclose(chd_rfile);
chd_rfile = NULL;
}
if (chd_hunk)
{
free(chd_hunk);
chd_hunk = NULL;
}
chd_hunkbytes = 0;
chd_hunknum = -1;
memset(chd_frame0, 0, sizeof(chd_frame0));
memset(chd_virtual_pregap, 0, sizeof(chd_virtual_pregap));
}
static bool ParseCHD(const char *chdPath)
{
const chd_header *head;
chd_error err;
char metadata[256];
int i;
uint32_t sessionnum;
uint32_t discLBA;
uint32_t chdFrames;
int prevSession;
bool saw_chse;
bool all_audio;
bool warn_virtual;
const uint32_t INTER_SESSION_GAP = 11400;
CDIntfCloseCHD();
memset(&disc, 0, sizeof(disc));
/* Prefer libchdr's own path open (real FILE* inside unity.c). This TU
* remaps FILE to RFILE via file_stream_transforms.h, so we must not
* pass our RFILE to chd_open_file(). Android content URIs fail that
* path and succeed through VFS callbacks below. */
err = chd_open(chdPath, CHD_OPEN_READ, NULL, &chd);
if (err != CHDERR_NONE)
{
chd_rfile = rfopen(chdPath, "rb");
if (!chd_rfile)
{
LOG_ERR("[CD-CHD] cannot open '%s' (%s)\n",
chdPath, chd_error_string(err));
return false;
}
err = chd_open_core_file_callbacks(&chd_vfs_cb, chd_rfile,
CHD_OPEN_READ, NULL, &chd);
if (err != CHDERR_NONE)
{
LOG_ERR("[CD-CHD] chd_open failed: %s\n", chd_error_string(err));
CDIntfCloseCHD();
return false;
}
}
head = chd_get_header(chd);
if (!head || head->hunkbytes == 0 || (head->hunkbytes % CD_FRAME_SIZE) != 0)
{
LOG_ERR("[CD-CHD] not a CD CHD (hunk size %u)\n",
head ? head->hunkbytes : 0);
CDIntfCloseCHD();
return false;
}
chd_hunkbytes = head->hunkbytes;
chd_hunk = (uint8_t *)malloc(chd_hunkbytes);
if (!chd_hunk)
{
CDIntfCloseCHD();
return false;
}
chd_hunknum = -1;
sessionnum = 1;
discLBA = 0;
chdFrames = 0;
prevSession = 0;
saw_chse = false;
all_audio = true;
warn_virtual = false;
for (i = 0; i < CDINTF_MAX_TRACKS; i++)
{
int tracknum, frames, pregap, postgap;
int sess;
char type[16], subtype[16], pgtype[16], pgsub[16];
uint32_t stored_pregap;
uint32_t padded;
type[0] = subtype[0] = pgtype[0] = pgsub[0] = '\0';
tracknum = frames = pregap = postgap = 0;
sess = 0;
metadata[0] = '\0';
if (chd_get_metadata(chd, CDROM_SESSION_METADATA_TAG, (uint32_t)i,
metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
{
sess = 1;
if (sscanf(metadata, CDROM_SESSION_METADATA_FORMAT, &sess) == 1 && sess >= 1)
{
sessionnum = sess;
saw_chse = true;
}
}
metadata[0] = '\0';
if (chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, (uint32_t)i,
metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
{
if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT,
&tracknum, type, subtype, &frames,
&pregap, pgtype, pgsub, &postgap) != 8)
break;
}
else if (chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, (uint32_t)i,
metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
{
if (sscanf(metadata, CDROM_TRACK_METADATA_FORMAT,
&tracknum, type, subtype, &frames) != 4)
break;
pregap = 0;
}
else
break;
if (tracknum != i + 1 || frames < 0 || pregap < 0)
{
LOG_ERR("[CD-CHD] bad track metadata at index %d\n", i);
CDIntfCloseCHD();
return false;
}
disc.tracks[i].number = (uint32_t)tracknum;
disc.tracks[i].session = sessionnum;
disc.tracks[i].sectorSize = 2352;
snprintf(disc.tracks[i].binFilePath, sizeof(disc.tracks[i].binFilePath),
"%s", chdPath);
if (strcmp(type, "AUDIO") == 0)
disc.tracks[i].type = CDINTF_TRACK_AUDIO;
else if (strncmp(type, "MODE1", 5) == 0)
{
disc.tracks[i].type = CDINTF_TRACK_MODE1;
all_audio = false;
}
else
{
disc.tracks[i].type = CDINTF_TRACK_MODE2;
all_audio = false;
}
/* Virtual pregaps (PGTYPE starts with V) are silence, not stored.
* Real pregaps live at the start of FRAMES. */
stored_pregap = (uint32_t)pregap;
if (pgtype[0] == 'V')
{
chd_virtual_pregap[i] = 1;
warn_virtual = true;
}
else
{
chd_virtual_pregap[i] = 0;
/* Pregap bytes are inside FRAMES; still use PREGAP for INDEX 01. */
}
if (prevSession != 0 && (int)sessionnum > prevSession)
{
/* MAME/CHD may encode the ~11400-sector session gap as a
* per-track pregap. Only synthesize it when the metadata did
* not already account for it. */
if (stored_pregap < 10000)
discLBA += INTER_SESSION_GAP;
}
prevSession = (int)sessionnum;
disc.tracks[i].startLBA = discLBA;
disc.tracks[i].dataLBA = discLBA + stored_pregap;
if (chd_virtual_pregap[i])
disc.tracks[i].lengthLBA = stored_pregap + (uint32_t)frames;
else
disc.tracks[i].lengthLBA = (uint32_t)frames;
MSFFromLBA(disc.tracks[i].dataLBA,
&disc.tracks[i].startM,
&disc.tracks[i].startS,
&disc.tracks[i].startF);
chd_frame0[i] = chdFrames;
padded = (uint32_t)(((frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING)
* CD_TRACK_PADDING);
chdFrames += padded;
discLBA += disc.tracks[i].lengthLBA;
disc.numTracks = (uint32_t)(i + 1);
}
if (disc.numTracks == 0)
{
LOG_ERR("[CD-CHD] no tracks in '%s'\n", chdPath);
CDIntfCloseCHD();
return false;
}
/* Old chdman flattened Jaguar CD's two sessions and wrote no CHSE.
* Those images cannot reconstruct the session-2 LBA; refuse them. */
if (!saw_chse && all_audio && disc.numTracks >= 2)
{
LOG_ERR("[CD-CHD] this CHD has no session metadata (CHSE). "
"It was almost certainly made with an old chdman that flattened "
"Jaguar CD's two sessions. Reconvert from CUE/BIN or CDI with "
"tools/jagcd (https://github.com/libretro/virtualjaguar-libretro/issues/322).\n");
CDIntfCloseCHD();
return false;
}
if (warn_virtual)
{
LOG_WRN("[CD-CHD] one or more tracks use virtual (silent) pregaps. "
"HLE boot is fine; real-BIOS authentication may fail. "
"CDI-class dumps preserve pregap audio; see docs/jagcd-chd.md.\n");
}
CDIntfFinalizeSessions();
snprintf(disc.binPath, sizeof(disc.binPath), "%s", chdPath);
disc.loaded = true;
LOG_INF("[CD-CHD] loaded %u track(s), %u session(s) from %s\n",
disc.numTracks, disc.numSessions, chdPath);
return true;
}
static bool CDIntfReadBlockCHD(uint32_t sector, uint8_t *buffer)
{
int i;
struct CDIntfTrack *track;
uint32_t rel;
uint32_t frame;
uint32_t frames_per_hunk;
uint32_t hunknum;
uint32_t offs;
chd_error err;
uint32_t b;
uint8_t tmp;
track = NULL;
for (i = (int)disc.numTracks - 1; i >= 0; i--)
{
uint32_t tStart = disc.tracks[i].startLBA;
uint32_t tEnd = tStart + disc.tracks[i].lengthLBA;
if (sector >= tStart && sector < tEnd)
{
track = &disc.tracks[i];
break;
}
}
if (!track)
{
memset(buffer, 0, 2352);
lastReadVirtualPregap = true;
lastVirtualPregapLBA = sector;
return true;
}
lastReadVirtualPregap = false;
if (chd_virtual_pregap[track->number - 1] && sector < track->dataLBA)
{
memset(buffer, 0, 2352);
return true;
}
if (chd_virtual_pregap[track->number - 1])
rel = sector - track->dataLBA;
else
rel = sector - track->startLBA;
frame = chd_frame0[track->number - 1] + rel;
frames_per_hunk = chd_hunkbytes / CD_FRAME_SIZE;
hunknum = frame / frames_per_hunk;
offs = (frame % frames_per_hunk) * CD_FRAME_SIZE;
if ((int32_t)hunknum != chd_hunknum)
{
err = chd_read(chd, hunknum, chd_hunk);
if (err != CHDERR_NONE)
{
memset(buffer, 0, 2352);
return false;
}
chd_hunknum = (int32_t)hunknum;
}
memcpy(buffer, chd_hunk + offs, 2352);
/* CHD audio frames are host/LE PCM; Jaguar CUE/BINs are I2S
* byte-swapped 16-bit words. Restore BIN order so HLE extract and
* the disc stream match ParseCueSheet. */
if (track->type == CDINTF_TRACK_AUDIO)
{
for (b = 0; b < 2352; b += 2)
{
tmp = buffer[b];
buffer[b] = buffer[b + 1];
buffer[b + 1] = tmp;
}
}
return true;
}
// Parse a CUE sheet and populate the disc structure
static bool ParseCueSheet(const char *cuePath)
{
@@ -414,70 +868,7 @@ static bool ParseCueSheet(const char *cuePath)
}
}
// Build session info
{
int i;
uint32_t sess1Min = 99, sess1Max = 0;
uint32_t sess2Min = 99, sess2Max = 0;
disc.numSessions = 1;
for (i = 0; i < (int)disc.numTracks; i++)
{
uint32_t trackNum = disc.tracks[i].number;
uint32_t sess = disc.tracks[i].session;
if (sess == 1)
{
if (trackNum < sess1Min) sess1Min = trackNum;
if (trackNum > sess1Max) sess1Max = trackNum;
}
else if (sess == 2)
{
disc.numSessions = 2;
if (trackNum < sess2Min) sess2Min = trackNum;
if (trackNum > sess2Max) sess2Max = trackNum;
}
}
// Session 1
disc.sessions[0].number = 1;
disc.sessions[0].firstTrack = (sess1Min <= CDINTF_MAX_TRACKS) ? sess1Min : 1;
disc.sessions[0].lastTrack = (sess1Max > 0) ? sess1Max : 1;
// Session 1 lead-out: start of session 2 first track, or end of session 1 last track
if (disc.numSessions >= 2 && sess2Min <= CDINTF_MAX_TRACKS)
{
uint32_t leadOut = disc.tracks[sess2Min - 1].startLBA;
disc.sessions[0].leadOutLBA = leadOut;
MSFFromLBA(leadOut, &disc.sessions[0].leadOutM,
&disc.sessions[0].leadOutS, &disc.sessions[0].leadOutF);
}
else
{
// Single session: lead-out after last track
uint32_t lastIdx = disc.sessions[0].lastTrack - 1;
uint32_t leadOut = disc.tracks[lastIdx].startLBA + disc.tracks[lastIdx].lengthLBA;
disc.sessions[0].leadOutLBA = leadOut;
MSFFromLBA(leadOut, &disc.sessions[0].leadOutM,
&disc.sessions[0].leadOutS, &disc.sessions[0].leadOutF);
}
// Session 2
if (disc.numSessions >= 2)
{
uint32_t lastIdx, leadOut;
disc.sessions[1].number = 2;
disc.sessions[1].firstTrack = sess2Min;
disc.sessions[1].lastTrack = sess2Max;
lastIdx = sess2Max - 1;
leadOut = disc.tracks[lastIdx].startLBA + disc.tracks[lastIdx].lengthLBA;
disc.sessions[1].leadOutLBA = leadOut;
MSFFromLBA(leadOut, &disc.sessions[1].leadOutM,
&disc.sessions[1].leadOutS, &disc.sessions[1].leadOutF);
}
}
CDIntfFinalizeSessions();
{
int i;
@@ -1266,6 +1657,9 @@ bool CDIntfOpenImage(const char *path)
if (ext && strcasecmp(ext + 1, "cdi") == 0)
return ParseCDI(path);
if (ext && strcasecmp(ext + 1, "chd") == 0)
return ParseCHD(path);
/* Bare ISO is refused deliberately: a 2048-byte-sector image cannot
* carry the multi-session layout a Jaguar CD requires (session 1 audio
* warning, session 2 data-as-audio 2352-byte tracks, lead-in offsets),
@@ -1304,6 +1698,8 @@ bool CDIntfOpenImage(const char *path)
void CDIntfCloseImage(void)
{
CDIntfCloseCHD();
if (cdi_file)
{
rfclose(cdi_file);
@@ -1329,6 +1725,8 @@ bool CDIntfIsImageLoaded(void)
{
if (!disc.loaded)
return false;
if (chd)
return true;
if (cdi_file)
return true;
// Multi-file CUE: binFile is NULL, but tracks have their own file paths
@@ -1363,6 +1761,9 @@ bool CDIntfReadBlock(uint32_t sector, uint8_t *buffer)
if (cdi_file)
return CDIntfReadBlockCDI(sector, buffer);
if (chd)
return CDIntfReadBlockCHD(sector, buffer);
// Find which track contains this sector. A sector belongs to a track only
// if it falls within [startLBA, startLBA + lengthLBA). Sectors in the
// inter-session gap belong to no track and are returned as silence.
@@ -1726,6 +2127,8 @@ bool CDIntfExtractBootStub(uint8_t *outBuf, uint32_t outBufSize,
int64_t bytesRead;
int64_t trackFileBase;
uint32_t loadAddr, length;
uint32_t nsec;
uint32_t s;
if (!disc.loaded || disc.numSessions < 2)
{
@@ -1743,13 +2146,33 @@ bool CDIntfExtractBootStub(uint8_t *outBuf, uint32_t outBufSize,
break;
}
}
if (!foundS2 || (!disc.tracks[firstS2Idx].binFilePath[0] && !disc.binPath[0]))
if (!foundS2 || (!chd && !disc.tracks[firstS2Idx].binFilePath[0] && !disc.binPath[0]))
{
LOG_WRN("[CD-BOOTSTUB] No session-2 track found (foundS2=%d, pathEmpty=%d)\n",
foundS2, foundS2 ? !disc.tracks[firstS2Idx].binFilePath[0] : -1);
return false;
}
bytesRead = 0;
if (chd)
{
/* The track "BIN" path is the CHD container. Stream INDEX 01
* through the same TOC used at runtime. */
LOG_INF("[CD-BOOTSTUB] Reading session-2 track %u from CHD at LBA %u\n",
disc.tracks[firstS2Idx].number,
disc.tracks[firstS2Idx].dataLBA);
nsec = (uint32_t)(sizeof(raw) / 2352);
for (s = 0; s < nsec; s++)
{
if (!CDIntfReadBlock(disc.tracks[firstS2Idx].dataLBA + s,
raw + s * 2352u))
break;
bytesRead += 2352;
}
LOG_INF("[CD-BOOTSTUB] Read %lld bytes from CHD TOC\n", (long long)bytesRead);
}
else
{
/* Both layouts start the track REGION with the track's pregap, and the
* Atari boot header sits at the start of the user data (INDEX 01), so the
* pregap has to be skipped -- otherwise we read pregap silence and the
@@ -1768,8 +2191,8 @@ bool CDIntfExtractBootStub(uint8_t *outBuf, uint32_t outBufSize,
* every CUE in the test corpus has INDEX 01 00:00:00 on its first
* session-2 track. DiscJuggler does store it (150 sectors on the
* session-2 track of the ROM-set Baldies image). */
{
uint32_t pregapSectors = 0;
uint32_t pregapSectors;
pregapSectors = 0;
if (disc.tracks[firstS2Idx].dataLBA > disc.tracks[firstS2Idx].startLBA)
pregapSectors = disc.tracks[firstS2Idx].dataLBA -
disc.tracks[firstS2Idx].startLBA;
@@ -1799,19 +2222,19 @@ bool CDIntfExtractBootStub(uint8_t *outBuf, uint32_t outBufSize,
(unsigned long long)trackFileBase);
trackFile = rfopen(disc.binPath, "rb");
}
}
if (!trackFile)
{
LOG_ERR("[CD-BOOTSTUB] rfopen failed for %s\n",
disc.tracks[firstS2Idx].binFilePath[0] ?
disc.tracks[firstS2Idx].binFilePath : disc.binPath);
return false;
}
if (!trackFile)
{
LOG_ERR("[CD-BOOTSTUB] rfopen failed for %s\n",
disc.tracks[firstS2Idx].binFilePath[0] ?
disc.tracks[firstS2Idx].binFilePath : disc.binPath);
return false;
}
rfseek(trackFile, trackFileBase, SEEK_SET);
bytesRead = rfread(raw, 1, sizeof(raw), trackFile);
rfclose(trackFile);
LOG_INF("[CD-BOOTSTUB] Read %lld bytes from track BIN\n", (long long)bytesRead);
rfseek(trackFile, trackFileBase, SEEK_SET);
bytesRead = rfread(raw, 1, sizeof(raw), trackFile);
rfclose(trackFile);
LOG_INF("[CD-BOOTSTUB] Read %lld bytes from track BIN\n", (long long)bytesRead);
}
if (bytesRead < 0x6A + 4)
{
LOG_ERR("[CD-BOOTSTUB] Too few bytes read (%lld < %d)\n",
+1
View File
@@ -70,6 +70,7 @@ static inline int cd_disc_priority(const char *ext)
if (cd_str_iequals(ext, "cue")) return 0;
if (cd_str_iequals(ext, "iso")) return 1;
if (cd_str_iequals(ext, "cdi")) return 2;
if (cd_str_iequals(ext, "chd")) return 3;
return -1;
}
+8
View File
@@ -0,0 +1,8 @@
# Synthetic Jaguar CD CHD fixtures (issue #322)
#
# synth_jagcd.chd — two-session, CHSE present (pinned chdman, -c none)
# synth_jagcd_nosession.chd — same layout, no CHSE (chdman 0.288, -c none)
#
# Regenerated from a 4+8 sector silence CUE plus an I2S-swapped ATARI boot
# header in session 2. Uncompressed so tiny FLAC/LZMA CD hunks cannot
# confuse libchdr. Do not recompress with createcd's default codecs.
Binary file not shown.
Binary file not shown.
+132
View File
@@ -0,0 +1,132 @@
/*
* test_cd_chd.c -- CHD session gate (issue #322)
*
* good : test/roms/synth_jagcd.chd (CHSE, two sessions) must load
* nosession : test/roms/synth_jagcd_nosession.chd must be refused
*
* Fixtures are uncompressed CD CHDs generated with the pinned chdman
* (CHSE) and Homebrew 0.288 (no CHSE). If a fixture is missing, the
* test can rebuild it when JAGCD_CHDMAN / chdman is on PATH; otherwise
* SKIP rather than fail a checkout that forgot to git-add the files.
*
* Build: make TEST_EXPORTS=1 test/test_cd_chd
*/
#include "cd_assertions.h"
#include "../libretro-common/include/libretro.h"
#include <unistd.h>
#include <sys/stat.h>
static struct vj_core C;
static bool file_exists(const char *path)
{
struct stat st;
return stat(path, &st) == 0 && st.st_size > 0;
}
static bool cd_load_game(const char *path)
{
struct retro_game_info info;
bool (*p_retro_load_game)(const struct retro_game_info *);
memset(&info, 0, sizeof(info));
info.path = path;
p_retro_load_game = (bool (*)(const struct retro_game_info *))
dlsym(C.handle, "retro_load_game");
if (!p_retro_load_game) return false;
return p_retro_load_game(&info);
}
static void cd_unload_game(void)
{
void (*p)(void) = (void (*)(void))dlsym(C.handle, "retro_unload_game");
if (p) p();
}
static uint32_t cd_num_sessions(void)
{
uint32_t (*p)(void) = (uint32_t (*)(void))dlsym(C.handle, "CDIntfGetNumSessions");
if (!p) return 0;
return p();
}
static bool env_cb(unsigned cmd, void *data)
{
if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE) {
struct retro_variable *var = (struct retro_variable *)data;
if (strcmp(var->key, "virtualjaguar_cd_boot_mode") == 0) {
var->value = "hle";
return true;
}
return false;
}
if (cmd == RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY) {
*(const char **)data = ".";
return true;
}
if (cmd == RETRO_ENVIRONMENT_SET_PIXEL_FORMAT) return true;
return false;
}
static void vcb(const void *d, unsigned w, unsigned h, size_t p)
{ (void)d; (void)w; (void)h; (void)p; }
static void acb(int16_t l, int16_t r) { (void)l; (void)r; }
static size_t abcb(const int16_t *d, size_t f) { (void)d; return f; }
static void ipcb(void) {}
static int16_t iscb(unsigned p, unsigned d, unsigned i, unsigned id)
{ (void)p; (void)d; (void)i; (void)id; return 0; }
TEST(chd_without_chse_is_refused)
{
const char *fix = "test/roms/synth_jagcd_nosession.chd";
if (!file_exists(fix)) {
SKIP_TEST(chd_without_chse_is_refused, "missing test/roms/synth_jagcd_nosession.chd");
return;
}
ASSERT_TRUE(!cd_load_game(fix));
cd_unload_game();
}
TEST(chd_with_chse_has_two_sessions)
{
const char *fix = "test/roms/synth_jagcd.chd";
if (!file_exists(fix)) {
SKIP_TEST(chd_with_chse_has_two_sessions, "missing test/roms/synth_jagcd.chd");
return;
}
ASSERT_TRUE(cd_load_game(fix));
ASSERT_EQ_U32(cd_num_sessions(), 2u);
cd_unload_game();
}
int main(int argc, char *argv[])
{
(void)argc; (void)argv;
TEST_INIT("CD CHD session gate");
if (!vj_core_load(&C)) {
fprintf(stderr, "FATAL: failed to load core\n");
return 1;
}
C.retro_set_environment(env_cb);
C.retro_set_video_refresh(vcb);
C.retro_set_audio_sample(acb);
C.retro_set_audio_sample_batch(abcb);
C.retro_set_input_poll(ipcb);
C.retro_set_input_state(iscb);
C.retro_init();
RUN_TEST(chd_without_chse_is_refused);
RUN_TEST(chd_with_chse_has_two_sessions);
{
void (*p)(void) = (void (*)(void))dlsym(C.handle, "retro_deinit");
if (p) p();
}
if (C.handle) dlclose(C.handle);
return TEST_REPORT();
}
+19
View File
@@ -598,6 +598,25 @@ done
run_title "baldies.cdi" "hle" "cdi"
run_title "baldies.cdi" "bios" "cdi"
# CHD conversions (issue #322). A sibling .chd of a matrix CUE, or a
# named homebrew dump, is measured the same way as CUE/CDI. Missing
# files are skipped -- CHD is produced locally with tools/jagcd, not
# shipped in the private tree by default.
chd_basename_exists() {
find -L "$ROMS_ROOT" -iname "$1" 2>/dev/null | grep -q .
}
for t in "${CUE_TITLES[@]}"; do
chd="${t%.cue}.chd"
if chd_basename_exists "$chd"; then
run_title "$chd" "hle" "chd"
run_title "$chd" "bios" "chd"
fi
done
if chd_basename_exists "Frog Feast (USA) (Unl).chd"; then
run_title "Frog Feast (USA) (Unl).chd" "hle" "chd"
run_title "Frog Feast (USA) (Unl).chd" "bios" "chd"
fi
# Loose ISOs: support was removed entirely (CDIntf refuses .iso at load
# with a LOG_ERR explaining why -- no session/track layout means no retail
# title can boot; see docs/cd-known-issues.md). No matrix row: there is
+36
View File
@@ -0,0 +1,36 @@
#!/bin/sh
# Optional round-trip: synth CUE -> CHD (CHSE-capable chdman) -> jagcd-chd-check.
# Exit 77 if no suitable chdman.
set -e
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)
CHDMAN=${JAGCD_CHDMAN:-}
if [ -z "$CHDMAN" ]; then
if [ -x "$ROOT/tools/jagcd/chdman" ]; then
CHDMAN=$ROOT/tools/jagcd/chdman
else
CHDMAN=$(command -v chdman || true)
fi
fi
[ -n "$CHDMAN" ] || { echo "SKIP: no chdman"; exit 77; }
TMP=$(mktemp -d /tmp/jagcd-roundtrip.XXXXXX)
trap 'rm -rf "$TMP"' EXIT
dd if=/dev/zero of="$TMP/t1.bin" bs=2352 count=4 status=none 2>/dev/null || dd if=/dev/zero of="$TMP/t1.bin" bs=2352 count=4
dd if=/dev/zero of="$TMP/t2.bin" bs=2352 count=8 status=none 2>/dev/null || dd if=/dev/zero of="$TMP/t2.bin" bs=2352 count=8
printf '%s\n' \
'REM SESSION 01' \
'FILE "t1.bin" BINARY' \
' TRACK 01 AUDIO' \
' INDEX 01 00:00:00' \
'REM SESSION 02' \
'FILE "t2.bin" BINARY' \
' TRACK 02 AUDIO' \
' INDEX 01 00:00:00' > "$TMP/disc.cue"
export JAGCD_CHDMAN=$CHDMAN
if ! sh "$ROOT/tools/jagcd/jagcd-to-chd" "$TMP/disc.cue" "$TMP/disc.chd"; then
echo "SKIP or FAIL: chdman could not write CHSE (too old?)"
exit 77
fi
echo "round-trip ok: $TMP/disc.chd"
exit 0
+4
View File
@@ -0,0 +1,4 @@
# MAME commit used to build the chdman we ship.
# Must include mamedev/mame#15886 (CHSE session metadata).
# Update this file and .github/workflows/chdman-tools.yml together.
420968ca7a722418b27cbcaf5a332f83bde4a9be
+144
View File
@@ -0,0 +1,144 @@
/*
* jagcd-chd-check -- inspect a CHD for Jaguar CD session metadata.
*
* Exit:
* 0 loadable (CHSE present, or not Jaguar-shaped)
* 1 missing CHSE on a Jaguar-shaped (all-audio, 2+ tracks) image
* 2 CHSE present but one or more tracks use virtual (silent) pregaps
* 3 not a CD CHD / open error
*
* Build:
* cc -O2 -std=c99 -I../../deps/libchdr/include \
* -I../../deps/libchdr/deps/lzma-25.01/include \
* -o jagcd-chd-check jagcd-chd-check.c ../../deps/libchdr/unity.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libchdr/chd.h>
#include <libchdr/cdrom.h>
#ifndef CDROM_SESSION_METADATA_TAG
#define CDROM_SESSION_METADATA_TAG CHD_MAKE_TAG('C','H','S','E')
#endif
#ifndef CDROM_SESSION_METADATA_FORMAT
#define CDROM_SESSION_METADATA_FORMAT "SESSION:%d"
#endif
int main(int argc, char **argv)
{
chd_file *chd;
chd_error err;
const chd_header *head;
char metadata[256];
int i, ntracks, saw_chse, all_audio, warn_virtual, max_sess;
uint32_t sessionnum;
if (argc != 2)
{
fprintf(stderr, "usage: %s <file.chd>\n", argv[0]);
return 3;
}
err = chd_open(argv[1], CHD_OPEN_READ, NULL, &chd);
if (err != CHDERR_NONE)
{
fprintf(stderr, "open failed: %s\n", chd_error_string(err));
return 3;
}
head = chd_get_header(chd);
if (!head || head->hunkbytes == 0 || (head->hunkbytes % CD_FRAME_SIZE) != 0)
{
fprintf(stderr, "not a CD CHD (hunkbytes=%u)\n",
head ? head->hunkbytes : 0);
chd_close(chd);
return 3;
}
sessionnum = 1;
ntracks = 0;
saw_chse = 0;
all_audio = 1;
warn_virtual = 0;
max_sess = 1;
printf("file: %s\n", argv[1]);
printf("hunkbytes: %u\n", head->hunkbytes);
for (i = 0; i < 99; i++)
{
int tracknum, frames, pregap, postgap, sess;
char type[16], subtype[16], pgtype[16], pgsub[16];
type[0] = subtype[0] = pgtype[0] = pgsub[0] = '\0';
tracknum = frames = pregap = postgap = 0;
sess = 0;
metadata[0] = '\0';
if (chd_get_metadata(chd, CDROM_SESSION_METADATA_TAG, (uint32_t)i,
metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
{
sess = 1;
if (sscanf(metadata, CDROM_SESSION_METADATA_FORMAT, &sess) == 1 && sess >= 1)
{
sessionnum = (uint32_t)sess;
saw_chse = 1;
}
}
metadata[0] = '\0';
if (chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, (uint32_t)i,
metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
{
if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT,
&tracknum, type, subtype, &frames,
&pregap, pgtype, pgsub, &postgap) != 8)
break;
}
else if (chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, (uint32_t)i,
metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)
{
if (sscanf(metadata, CDROM_TRACK_METADATA_FORMAT,
&tracknum, type, subtype, &frames) != 4)
break;
}
else
break;
if (strcmp(type, "AUDIO") != 0)
all_audio = 0;
if (pgtype[0] == 'V')
warn_virtual = 1;
if ((int)sessionnum > max_sess)
max_sess = (int)sessionnum;
printf("track %d session=%u type=%s frames=%d pregap=%d pgtype=%s\n",
tracknum, sessionnum, type, frames, pregap,
pgtype[0] ? pgtype : "-");
ntracks++;
}
chd_close(chd);
printf("tracks: %d\n", ntracks);
printf("chse: %s\n", saw_chse ? "yes" : "no");
printf("sessions_seen: %d\n", max_sess);
printf("virtual_pregap: %s\n", warn_virtual ? "yes" : "no");
if (ntracks == 0)
return 3;
if (!saw_chse && all_audio && ntracks >= 2)
{
fprintf(stderr, "REFUSE: Jaguar-shaped CHD with no CHSE. "
"Reconvert with tools/jagcd (issue #322).\n");
return 1;
}
if (warn_virtual)
{
fprintf(stderr, "WARN: virtual (silent) pregaps; HLE is fine, BIOS auth may fail.\n");
return 2;
}
return 0;
}
+66
View File
@@ -0,0 +1,66 @@
#!/bin/sh
# Convert a Jaguar CD CUE/BIN to CHD with a chdman that writes CHSE.
# Usage: jagcd-to-chd INPUT.cue [OUTPUT.chd]
set -e
die() { echo "jagcd-to-chd: $*" >&2; exit 1; }
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
PIN_FILE="$SCRIPT_DIR/CHDMAN_PIN"
PIN=$(grep -E '^[0-9a-f]{7,}' "$PIN_FILE" | tail -n 1)
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
die "usage: $0 INPUT.cue [OUTPUT.chd]"
fi
IN=$1
case "$IN" in
*.cue|*.CUE|*.toc|*.TOC) ;;
*.cdi|*.CDI) die "chdman createcd does not read CDI; convert to CUE/BIN first (or use the core's CDI loader directly)" ;;
*) die "input must be a .cue (or .toc)" ;;
esac
[ -f "$IN" ] || die "not found: $IN"
OUT=$2
if [ -z "$OUT" ]; then
OUT=$(printf '%s' "$IN" | sed 's/\.[Cc][Uu][Ee]$/.chd/;s/\.[Tt][Oo][Cc]$/.chd/')
fi
if [ -n "$JAGCD_CHDMAN" ]; then
CHDMAN=$JAGCD_CHDMAN
elif [ -x "$SCRIPT_DIR/chdman" ]; then
CHDMAN=$SCRIPT_DIR/chdman
elif [ -x "$SCRIPT_DIR/chdman.exe" ]; then
CHDMAN=$SCRIPT_DIR/chdman.exe
else
CHDMAN=$(command -v chdman || true)
fi
[ -n "$CHDMAN" ] || die "chdman not found. Set JAGCD_CHDMAN or put chdman next to this script (pin $PIN)."
CHECK=$SCRIPT_DIR/jagcd-chd-check
if [ ! -x "$CHECK" ]; then
CHECK=$SCRIPT_DIR/jagcd-chd-check.exe
fi
echo "using $CHDMAN"
"$CHDMAN" createcd -i "$IN" -o "$OUT" -f
if [ -x "$CHECK" ]; then
set +e
"$CHECK" "$OUT"
st=$?
set -e
if [ "$st" -eq 1 ]; then
die "chdman did not write CHSE session tags (too old). Use the binary from this repo's GitHub release (pin $PIN), not Homebrew/distro chdman."
fi
# 0 = clean, 2 = virtual pregaps (warn, still a valid conversion)
exit "$st"
fi
# Fallback when the checker is not built: require dumpmeta CHSE on track 0 or 1.
if "$CHDMAN" dumpmeta -i "$OUT" -t CHSE -ix 0 >/dev/null 2>&1 \
|| "$CHDMAN" dumpmeta -i "$OUT" -t CHSE -ix 1 >/dev/null 2>&1; then
echo "CHSE present"
exit 0
fi
die "no CHSE tag in $OUT — chdman is too old (need pin $PIN / MAME with PR 15886)"
+37
View File
@@ -0,0 +1,37 @@
@echo off
REM Convert a Jaguar CD CUE/BIN to CHD. See jagcd-to-chd (the Unix script) for behavior.
setlocal
set "SCRIPT_DIR=%~dp0"
if "%~1"=="" (
echo usage: %~nx0 INPUT.cue [OUTPUT.chd]
exit /b 1
)
if defined JAGCD_CHDMAN (
set "CHDMAN=%JAGCD_CHDMAN%"
) else if exist "%SCRIPT_DIR%chdman.exe" (
set "CHDMAN=%SCRIPT_DIR%chdman.exe"
) else (
set "CHDMAN=chdman"
)
set "IN=%~1"
if "%~2"=="" (
set "OUT=%~dpn1.chd"
) else (
set "OUT=%~2"
)
"%CHDMAN%" createcd -i "%IN%" -o "%OUT%" -f
if errorlevel 1 exit /b 1
if exist "%SCRIPT_DIR%jagcd-chd-check.exe" (
"%SCRIPT_DIR%jagcd-chd-check.exe" "%OUT%"
exit /b %ERRORLEVEL%
)
"%CHDMAN%" dumpmeta -i "%OUT%" -t CHSE -ix 0 >nul 2>&1
if errorlevel 1 (
"%CHDMAN%" dumpmeta -i "%OUT%" -t CHSE -ix 1 >nul 2>&1
if errorlevel 1 (
echo no CHSE tag -- chdman is too old. See docs/jagcd-chd.md
exit /b 1
)
)
echo CHSE present
exit /b 0