mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 04:36:00 -10:00
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:
1 parent
9b4c604eab
commit
6453b22baa
84 files changed
+61975
-101
No files matched your search
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Vendored
+88
@@ -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
|
||||
Vendored
+170
@@ -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)
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
6cde534
|
||||
Vendored
+24
@@ -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.
|
||||
Vendored
+7
@@ -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/)).
|
||||
Vendored
+33
@@ -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
@@ -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
|
||||
+1487
File diff suppressed because it is too large.
Load diff
+341
@@ -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
|
||||
+1339
File diff suppressed because it is too large.
Load diff
+29
@@ -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()
|
||||
Vendored
+3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,2 @@
|
||||
#include "../include/LzmaDec.h"
|
||||
#include "real/LzmaDec.c"
|
||||
+1361
File diff suppressed because it is too large.
Load diff
+27
@@ -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()
|
||||
+7909
File diff suppressed because it is too large.
Load diff
+1510
File diff suppressed because it is too large.
Load diff
+7
@@ -0,0 +1,7 @@
|
||||
add_library(zstd STATIC
|
||||
zstd.h
|
||||
zstd_errors.h
|
||||
zstddeclib.c
|
||||
)
|
||||
|
||||
set_target_properties(zstd PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
Vendored
+3198
File diff suppressed because it is too large.
Load diff
+107
@@ -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 */
|
||||
+23644
File diff suppressed because it is too large.
Load diff
+12660
File diff suppressed because it is too large.
Load diff
+43
@@ -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
|
||||
Vendored
+117
@@ -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__ */
|
||||
Vendored
+430
@@ -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
@@ -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
@@ -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
|
||||
Vendored
+51
@@ -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
@@ -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
@@ -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 */
|
||||
Vendored
+28
@@ -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 */
|
||||
Vendored
+27
@@ -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 */
|
||||
Vendored
+26
@@ -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 */
|
||||
Vendored
+26
@@ -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 */
|
||||
Vendored
+22
@@ -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 */
|
||||
Vendored
+22
@@ -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 */
|
||||
Vendored
+33
@@ -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 */
|
||||
Vendored
+41
@@ -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 */
|
||||
Vendored
+27
@@ -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 */
|
||||
Vendored
+125
@@ -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;
|
||||
}
|
||||
|
||||
Vendored
+492
@@ -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, §or[ECC_P_OFFSET + byte], §or[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, §or[ECC_Q_OFFSET + byte], §or[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(§or[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
|
||||
memset(§or[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;
|
||||
}
|
||||
Vendored
+2255
File diff suppressed because it is too large.
Load diff
+100
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
}
|
||||
Vendored
+329
@@ -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;
|
||||
}
|
||||
Vendored
+569
@@ -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;
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
global: chd_*;
|
||||
local: *;
|
||||
};
|
||||
|
||||
Vendored
+36
@@ -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
@@ -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."
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Executable
+36
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Executable
+66
@@ -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)"
|
||||
@@ -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
|
||||
Reference in new issue
Block a user