Support MWCC 4.1+ series compilers (#1)
This implements support for MWCC 4.1+ (aka GC 3.0a3 and above). Notable changes: - Hook `CreateFileMappingA` / `MapViewOfFile`: These compilers use these functions rather than `ReadFile` for headers. - Hook `MultiByteToWideChar`: This function's behavior changed in Windows 2000 SP4 / Windows XP, and certain compiler versions rely on the old behavior. This fully reimplements it for CP-932 with the legacy behavior. - Explicitly fail on invalid UTF-8 inputs. Add a warning log when the Shift JIS output is invalid.
This commit is contained in:
parent
b85eab4cc8
commit
9120080eb5
|
@ -8,8 +8,9 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_BIN_NAME: sjiswrap
|
BUILD_PROFILE: release
|
||||||
CARGO_TARGET_DIR: target
|
CARGO_TARGET_DIR: target
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
|
@ -19,11 +20,14 @@ jobs:
|
||||||
RUSTFLAGS: -D warnings
|
RUSTFLAGS: -D warnings
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
|
toolchain: nightly-2023-09-18
|
||||||
|
- name: Cache Rust workspace
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
- name: Cargo check
|
- name: Cargo check
|
||||||
run: cargo check --features debug --all-targets
|
run: cargo check --features debug --all-targets
|
||||||
- name: Cargo clippy
|
- name: Cargo clippy
|
||||||
|
@ -36,53 +40,52 @@ jobs:
|
||||||
RUSTFLAGS: -D warnings
|
RUSTFLAGS: -D warnings
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
# We use nightly options in rustfmt.toml
|
# We use nightly options in rustfmt.toml
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
|
toolchain: nightly-2023-09-18
|
||||||
- name: Cargo fmt
|
- name: Cargo fmt
|
||||||
run: cargo fmt --all --check
|
run: cargo fmt --all --check
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
|
env:
|
||||||
|
CARGO_BIN_NAME: sjiswrap
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- platform: windows-latest
|
- platform: windows-latest
|
||||||
target: i686-pc-windows-msvc
|
target: i686-pc-windows-msvc
|
||||||
name: windows-x86
|
name: windows-x86
|
||||||
build: build
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Install dependencies
|
|
||||||
if: matrix.packages != ''
|
|
||||||
run: |
|
|
||||||
sudo apt-get -y update
|
|
||||||
sudo apt-get -y install ${{ matrix.packages }}
|
|
||||||
- name: Install cargo-zigbuild
|
|
||||||
if: matrix.build == 'zigbuild'
|
|
||||||
run: pip install ziglang==0.10.1.post1 cargo-zigbuild==0.17.0
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
components: rust-src
|
components: rust-src
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
- name: Cargo build
|
toolchain: nightly-2023-09-18
|
||||||
run: cargo ${{ matrix.build }} --release --features nightly --target ${{ matrix.target }} --bin ${{ env.CARGO_BIN_NAME }} -Z build-std=std,panic_abort
|
- name: Cache Rust workspace
|
||||||
- name: Upload artifacts
|
uses: Swatinem/rust-cache@v2
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.name }}
|
key: ${{ matrix.target }}
|
||||||
|
- name: Cargo build
|
||||||
|
run: >
|
||||||
|
cargo build --profile ${{ env.BUILD_PROFILE }} --target ${{ matrix.target }}
|
||||||
|
--bin ${{ env.CARGO_BIN_NAME }} -Z build-std=std,panic_abort --features nightly
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ env.CARGO_BIN_NAME }}-${{ matrix.name }}
|
||||||
path: |
|
path: |
|
||||||
${{ env.CARGO_TARGET_DIR }}/release/${{ env.CARGO_BIN_NAME }}
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}
|
||||||
${{ env.CARGO_TARGET_DIR }}/release/${{ env.CARGO_BIN_NAME }}.exe
|
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/${{ env.BUILD_PROFILE }}/${{ env.CARGO_BIN_NAME }}.exe
|
||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release/${{ env.CARGO_BIN_NAME }}
|
|
||||||
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release/${{ env.CARGO_BIN_NAME }}.exe
|
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
release:
|
release:
|
||||||
|
@ -90,20 +93,52 @@ jobs:
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [ build ]
|
needs: [ build ]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Check git tag against Cargo version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -eou pipefail
|
||||||
|
tag='${{github.ref}}'
|
||||||
|
tag="${tag#refs/tags/}"
|
||||||
|
version=$(grep '^version' Cargo.toml | head -1 | awk -F' = ' '{print $2}' | tr -d '"')
|
||||||
|
version="v$version"
|
||||||
|
if [ "$tag" != "$version" ]; then
|
||||||
|
echo "::error::Git tag doesn't match the Cargo version! ($tag != $version)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
- name: Rename artifacts
|
- name: Rename artifacts
|
||||||
working-directory: artifacts
|
working-directory: artifacts
|
||||||
run: |
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
mkdir ../out
|
mkdir ../out
|
||||||
for i in */*/release/$CARGO_BIN_NAME*; do
|
for dir in */; do
|
||||||
mv "$i" "../out/$(sed -E "s/([^/]+)\/[^/]+\/release\/($CARGO_BIN_NAME)/\2-\1/" <<< "$i")"
|
for file in "$dir"*; do
|
||||||
|
base=$(basename "$file")
|
||||||
|
name="${base%.*}"
|
||||||
|
ext="${base##*.}"
|
||||||
|
if [ "$ext" = "$base" ]; then
|
||||||
|
ext=""
|
||||||
|
else
|
||||||
|
ext=".$ext"
|
||||||
|
fi
|
||||||
|
arch="${dir%/}" # remove trailing slash
|
||||||
|
arch="${arch##"$name-"}" # remove bin name
|
||||||
|
dst="../out/${name}-${arch}${ext}"
|
||||||
|
mv "$file" "$dst"
|
||||||
|
done
|
||||||
done
|
done
|
||||||
ls -R ../out
|
ls -R ../out
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: out/*
|
files: out/*
|
||||||
|
draft: true
|
||||||
|
generate_release_notes: true
|
||||||
|
|
|
@ -4,15 +4,15 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.75"
|
version = "1.0.91"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -43,10 +43,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc62ccb14881da5d1862cda3a9648fb4a4897b2aff0b2557b89da44a5e550b7c"
|
checksum = "bc62ccb14881da5d1862cda3a9648fb4a4897b2aff0b2557b89da44a5e550b7c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num"
|
||||||
version = "0.2.16"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
|
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"libm",
|
"libm",
|
||||||
|
@ -63,40 +127,132 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "proc-macro2"
|
||||||
version = "1.1.0"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sjiswrap"
|
name = "sjiswrap"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"memexec",
|
"memexec",
|
||||||
|
"num",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "syn"
|
||||||
version = "0.48.0"
|
version = "2.0.85"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.58.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
|
||||||
|
dependencies = [
|
||||||
|
"windows-core",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.58.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.58.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.58.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-strings"
|
||||||
version = "0.48.5"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
|
||||||
|
dependencies = [
|
||||||
|
"windows-result",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm",
|
||||||
|
@ -105,42 +261,48 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -3,12 +3,13 @@ name = "sjiswrap"
|
||||||
description = "UTF-8 to Shift JIS wrapper for old compilers."
|
description = "UTF-8 to Shift JIS wrapper for old compilers."
|
||||||
authors = ["Luke Street <luke@street.dev>"]
|
authors = ["Luke Street <luke@street.dev>"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
repository = "https://github.com/encounter/sjiswrap"
|
repository = "https://github.com/encounter/sjiswrap"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
categories = ["command-line-utilities"]
|
categories = ["command-line-utilities"]
|
||||||
|
rust-version = "1.72.0"
|
||||||
|
|
||||||
# Size optimizations
|
# Size optimizations
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -25,13 +26,14 @@ nightly = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.72"
|
anyhow = "1.0"
|
||||||
encoding_rs = "0.8.32"
|
encoding_rs = "0.8"
|
||||||
memexec = { version = "0.2.0", features = ["hook"] }
|
memexec = { version = "0.2", features = ["hook"] }
|
||||||
rustc-hash = "1.1.0"
|
num = "0.4"
|
||||||
|
rustc-hash = "2.0"
|
||||||
|
|
||||||
[dependencies.windows]
|
[dependencies.windows]
|
||||||
version = "0.48.0"
|
version = "0.58"
|
||||||
features = [
|
features = [
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_Globalization",
|
"Win32_Globalization",
|
||||||
|
|
500
src/main.rs
500
src/main.rs
|
@ -9,26 +9,38 @@ use std::{
|
||||||
iter::{Cloned, Peekable},
|
iter::{Cloned, Peekable},
|
||||||
mem::MaybeUninit,
|
mem::MaybeUninit,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
pin::Pin,
|
||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use encoding_rs::SHIFT_JIS;
|
use encoding_rs::{SHIFT_JIS, UTF_8};
|
||||||
|
use num::Zero;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use windows::{
|
use windows::{
|
||||||
core::{PCSTR, PCWSTR},
|
core::{PCSTR, PCWSTR},
|
||||||
Win32::{
|
Win32::{
|
||||||
Foundation::{
|
Foundation::{
|
||||||
CloseHandle, GetLastError, SetLastError, BOOL, ERROR_INSUFFICIENT_BUFFER,
|
CloseHandle, GetLastError, SetLastError, BOOL, ERROR_INSUFFICIENT_BUFFER,
|
||||||
GENERIC_ACCESS_RIGHTS, GENERIC_READ, HANDLE, HMODULE, INVALID_HANDLE_VALUE,
|
ERROR_NO_UNICODE_TRANSLATION, ERROR_SUCCESS, GENERIC_ACCESS_RIGHTS, GENERIC_READ,
|
||||||
|
HANDLE, HMODULE, INVALID_HANDLE_VALUE,
|
||||||
},
|
},
|
||||||
|
Globalization::{MultiByteToWideChar, MULTI_BYTE_TO_WIDE_CHAR_FLAGS},
|
||||||
Security::SECURITY_ATTRIBUTES,
|
Security::SECURITY_ATTRIBUTES,
|
||||||
Storage::FileSystem::{
|
Storage::FileSystem::{
|
||||||
CreateFileA, GetFileSize, GetFullPathNameA, ReadFile, SetFilePointer, FILE_BEGIN,
|
CreateFileA, GetFileSize, GetFullPathNameA, ReadFile, ReadFileEx, SetFilePointer,
|
||||||
FILE_CREATION_DISPOSITION, FILE_CURRENT, FILE_END, FILE_FLAGS_AND_ATTRIBUTES,
|
FILE_BEGIN, FILE_CREATION_DISPOSITION, FILE_CURRENT, FILE_END,
|
||||||
FILE_SHARE_MODE, SET_FILE_POINTER_MOVE_METHOD,
|
FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, SET_FILE_POINTER_MOVE_METHOD,
|
||||||
|
},
|
||||||
|
System::{
|
||||||
|
Environment::GetCommandLineA,
|
||||||
|
LibraryLoader::SetDllDirectoryA,
|
||||||
|
Memory::{
|
||||||
|
CreateFileMappingA, MapViewOfFile, UnmapViewOfFile, FILE_MAP, FILE_MAP_ALL_ACCESS,
|
||||||
|
FILE_MAP_WRITE, MEMORY_MAPPED_VIEW_ADDRESS, PAGE_PROTECTION_FLAGS,
|
||||||
|
},
|
||||||
|
IO::{LPOVERLAPPED_COMPLETION_ROUTINE, OVERLAPPED},
|
||||||
},
|
},
|
||||||
System::{Environment::GetCommandLineA, LibraryLoader::SetDllDirectoryA, IO::OVERLAPPED},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,6 +73,15 @@ macro_rules! debug_println {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! fail {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
{
|
||||||
|
eprintln!($($arg)*);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args: Vec<OsString> = std::env::args_os().collect();
|
let args: Vec<OsString> = std::env::args_os().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
|
@ -99,10 +120,25 @@ fn main() -> Result<()> {
|
||||||
hooks.insert("kernel32.dll!GetFileSize".into(), hook_GetFileSize as *const c_void);
|
hooks.insert("kernel32.dll!GetFileSize".into(), hook_GetFileSize as *const c_void);
|
||||||
hooks.insert("kernel32.dll!CloseHandle".into(), hook_CloseHandle as *const c_void);
|
hooks.insert("kernel32.dll!CloseHandle".into(), hook_CloseHandle as *const c_void);
|
||||||
hooks.insert("kernel32.dll!ReadFile".into(), hook_ReadFile as *const c_void);
|
hooks.insert("kernel32.dll!ReadFile".into(), hook_ReadFile as *const c_void);
|
||||||
|
hooks.insert("kernel32.dll!ReadFileEx".into(), hook_ReadFileEx as *const c_void);
|
||||||
hooks.insert("kernel32.dll!SetFilePointer".into(), hook_SetFilePointer as *const c_void);
|
hooks.insert("kernel32.dll!SetFilePointer".into(), hook_SetFilePointer as *const c_void);
|
||||||
hooks.insert("kernel32.dll!IsDBCSLeadByte".into(), hook_IsDBCSLeadByte as *const c_void);
|
hooks.insert("kernel32.dll!IsDBCSLeadByte".into(), hook_IsDBCSLeadByte as *const c_void);
|
||||||
hooks
|
hooks
|
||||||
.insert("kernel32.dll!GetModuleFileNameA".into(), hook_GetModuleFileNameA as *const c_void);
|
.insert("kernel32.dll!GetModuleFileNameA".into(), hook_GetModuleFileNameA as *const c_void);
|
||||||
|
hooks
|
||||||
|
.insert("kernel32.dll!CreateFileMappingA".into(), hook_CreateFileMappingA as *const c_void);
|
||||||
|
hooks
|
||||||
|
.insert("kernel32.dll!CreateFileMappingW".into(), hook_CreateFileMappingW as *const c_void);
|
||||||
|
hooks.insert("kernel32.dll!OpenFileMappingA".into(), hook_OpenFileMappingA as *const c_void);
|
||||||
|
hooks.insert("kernel32.dll!OpenFileMappingW".into(), hook_OpenFileMappingW as *const c_void);
|
||||||
|
hooks.insert("kernel32.dll!MapViewOfFile".into(), hook_MapViewOfFile as *const c_void);
|
||||||
|
hooks.insert("kernel32.dll!MapViewOfFileEx".into(), hook_MapViewOfFileEx as *const c_void);
|
||||||
|
hooks.insert("kernel32.dll!UnmapViewOfFile".into(), hook_UnmapViewOfFile as *const c_void);
|
||||||
|
hooks.insert(
|
||||||
|
"kernel32.dll!MultiByteToWideChar".into(),
|
||||||
|
hook_MultiByteToWideChar as *const c_void,
|
||||||
|
);
|
||||||
|
hooks.insert("kernel32.dll!GetACP".into(), hook_GetACP as *const c_void);
|
||||||
unsafe { memexec::memexec_exe_with_hooks(&buf, &hooks) }.expect("Failed to execute");
|
unsafe { memexec::memexec_exe_with_hooks(&buf, &hooks) }.expect("Failed to execute");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -118,15 +154,21 @@ struct FileHandle {
|
||||||
struct GlobalState {
|
struct GlobalState {
|
||||||
exe_path: CString,
|
exe_path: CString,
|
||||||
cmdline: Option<CString>,
|
cmdline: Option<CString>,
|
||||||
encoded_files: FxHashMap<PathBuf, Vec<u8>>,
|
encoded_files: FxHashMap<PathBuf, Pin<Box<[u8]>>>,
|
||||||
file_handles: FxHashMap<isize, FileHandle>,
|
file_handles: FxHashMap<*mut c_void, FileHandle>,
|
||||||
|
file_mapping_handles: FxHashMap<*mut c_void, HANDLE>,
|
||||||
|
view_to_mapping: FxHashMap<*mut c_void, HANDLE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalState {
|
impl GlobalState {
|
||||||
fn file_by_handle(&mut self, handle: HANDLE) -> Option<(&mut FileHandle, &[u8])> {
|
fn file_by_handle(&mut self, handle: HANDLE) -> Option<(&mut FileHandle, Pin<&[u8]>)> {
|
||||||
self.file_handles
|
self.file_handles
|
||||||
.get_mut(&handle.0)
|
.get_mut(&handle.0)
|
||||||
.and_then(|file| self.encoded_files.get(&file.path).map(|data| (file, data.as_slice())))
|
.and_then(|file| self.encoded_files.get(&file.path).map(|data| (file, data.as_ref())))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file_by_mapping_handle(&mut self, handle: HANDLE) -> Option<(&mut FileHandle, Pin<&[u8]>)> {
|
||||||
|
self.file_mapping_handles.get(&handle.0).cloned().and_then(|file| self.file_by_handle(file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,7 +196,7 @@ extern "stdcall" fn hook_GetCommandLineA() -> PCSTR {
|
||||||
loop {
|
loop {
|
||||||
let Some(c) = iter.next() else {
|
let Some(c) = iter.next() else {
|
||||||
if quoted {
|
if quoted {
|
||||||
panic!("GetCommandLineA(): Unterminated quoted string");
|
fail!("sjiswrap: GetCommandLineA(): Unterminated quoted string");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
@ -162,8 +204,8 @@ extern "stdcall" fn hook_GetCommandLineA() -> PCSTR {
|
||||||
if c == b'"' {
|
if c == b'"' {
|
||||||
let next = iter.next();
|
let next = iter.next();
|
||||||
if next != Some(b' ') && next.is_some() {
|
if next != Some(b' ') && next.is_some() {
|
||||||
panic!(
|
fail!(
|
||||||
"GetCommandLineA(): Expected space after quote, got '{}'",
|
"sjiswrap: GetCommandLineA(): Expected space after quote, got '{}'",
|
||||||
char::from(next.unwrap())
|
char::from(next.unwrap())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -201,7 +243,7 @@ extern "stdcall" fn hook_GetCommandLineA() -> PCSTR {
|
||||||
|
|
||||||
/// `GetCommandLineW` hook. Currently unsupported.
|
/// `GetCommandLineW` hook. Currently unsupported.
|
||||||
extern "stdcall" fn hook_GetCommandLineW() -> PCSTR {
|
extern "stdcall" fn hook_GetCommandLineW() -> PCSTR {
|
||||||
panic!("GetCommandLineW() is not supported");
|
fail!("sjiswrap: GetCommandLineW() is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a file into memory and encode it as Shift JIS.
|
/// Read a file into memory and encode it as Shift JIS.
|
||||||
|
@ -220,32 +262,35 @@ fn encode_file(handle: HANDLE, path: &Path) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut data = vec![0u8; filesize as usize];
|
// Include a null terminator for MapViewOfFile
|
||||||
|
let mut data = vec![0u8; filesize as usize + 1];
|
||||||
let mut bytes_read = 0u32;
|
let mut bytes_read = 0u32;
|
||||||
if !unsafe {
|
if unsafe {
|
||||||
ReadFile(
|
let slice = &mut data[..filesize as usize];
|
||||||
handle,
|
ReadFile(handle, Some(slice), Some(&mut bytes_read), None)
|
||||||
Some(data.as_mut_ptr() as *mut c_void),
|
|
||||||
data.len() as u32,
|
|
||||||
Some(&mut bytes_read),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.as_bool()
|
.is_err()
|
||||||
|| bytes_read != filesize as u32
|
|| bytes_read != filesize as u32
|
||||||
{
|
{
|
||||||
|
eprintln!("sjiswrap: Failed to read file {}", path.display());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let str = unsafe { std::str::from_utf8_unchecked(&data) };
|
let str = match std::str::from_utf8(&data) {
|
||||||
let (encoded, _, _) = SHIFT_JIS.encode(str);
|
Ok(str) => str,
|
||||||
|
Err(e) => fail!("sjiswrap: File {} is not valid UTF-8: {}", path.display(), e),
|
||||||
|
};
|
||||||
|
let (encoded, _, error) = SHIFT_JIS.encode(str);
|
||||||
|
if error {
|
||||||
|
eprintln!("sjiswrap: File {} contains Shift JIS encoding errors", path.display());
|
||||||
|
}
|
||||||
match encoded {
|
match encoded {
|
||||||
Cow::Borrowed(_) => {
|
Cow::Borrowed(_) => {
|
||||||
// No modifications were made, use the original data
|
// No modifications were made, use the original data
|
||||||
entry.insert(data);
|
entry.insert(Pin::new(data.into_boxed_slice()));
|
||||||
}
|
}
|
||||||
Cow::Owned(data) => {
|
Cow::Owned(data) => {
|
||||||
entry.insert(data);
|
entry.insert(Pin::new(data.into_boxed_slice()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,21 +347,23 @@ extern "stdcall" fn hook_CreateFileW(
|
||||||
_dwFlagsAndAttributes: FILE_FLAGS_AND_ATTRIBUTES,
|
_dwFlagsAndAttributes: FILE_FLAGS_AND_ATTRIBUTES,
|
||||||
_hTemplateFile: HANDLE,
|
_hTemplateFile: HANDLE,
|
||||||
) -> HANDLE {
|
) -> HANDLE {
|
||||||
panic!("CreateFileW() is not supported");
|
fail!("sjiswrap: CreateFileW() is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `GetFileSize` hook. If the file was read into memory, return that size instead.
|
/// `GetFileSize` hook. If the file was read into memory, return that size instead.
|
||||||
extern "stdcall" fn hook_GetFileSize(hFile: HANDLE, lpFileSizeHigh: *mut u32) -> u32 {
|
extern "stdcall" fn hook_GetFileSize(hFile: HANDLE, lpFileSizeHigh: *mut u32) -> u32 {
|
||||||
if !hFile.is_invalid() {
|
if !hFile.is_invalid() {
|
||||||
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
if let Some((_handle, data)) = state.file_by_handle(hFile) {
|
if let Some((handle, data)) = state.file_by_handle(hFile) {
|
||||||
debug_println!("OVERRIDE: GetFileSize({:#X}) = {:#X}", hFile.0, data.len() as u32);
|
let _ = handle;
|
||||||
return data.len() as u32;
|
let file_size = data.len() as u32 - 1 /* null terminator */;
|
||||||
|
debug_println!("OVERRIDE: GetFileSize({}) = {:#X}", handle.path.display(), file_size);
|
||||||
|
return file_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = unsafe { GetFileSize(hFile, Some(lpFileSizeHigh)) };
|
let ret = unsafe { GetFileSize(hFile, Some(lpFileSizeHigh)) };
|
||||||
debug_println!("GetFileSize({:#X}, {:?}) = {:#X}", hFile.0, lpFileSizeHigh, ret);
|
debug_println!("GetFileSize({:p}, {:?}) = {:#X}", hFile.0, lpFileSizeHigh, ret);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,15 +373,18 @@ extern "stdcall" fn hook_CloseHandle(hObject: HANDLE) -> BOOL {
|
||||||
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
if let Some(handle) = state.file_handles.remove(&hObject.0) {
|
if let Some(handle) = state.file_handles.remove(&hObject.0) {
|
||||||
let _ = handle;
|
let _ = handle;
|
||||||
debug_println!("File handle removed: {:#X} ({})", hObject.0, handle.path.display());
|
debug_println!("File handle removed: {:p} ({})", hObject.0, handle.path.display());
|
||||||
// Purposefully leave the file data itself in the cache.
|
// Purposefully leave the file data itself in the cache.
|
||||||
// mwcceppc in particular will read the same file multiple times.
|
// mwcceppc in particular will read the same file multiple times.
|
||||||
}
|
}
|
||||||
|
if let Some(_mapping) = state.file_mapping_handles.remove(&hObject.0) {
|
||||||
|
debug_println!("File mapping handle removed: {:p}", hObject.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = unsafe { CloseHandle(hObject) };
|
let ret = unsafe { CloseHandle(hObject) }.is_ok();
|
||||||
debug_println!("CloseHandle({:#X}) = {:#X}", hObject.0, ret.0);
|
debug_println!("CloseHandle({:p}) = {}", hObject.0, ret);
|
||||||
ret
|
ret.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ReadFile` hook. If the file was read into memory, read from that instead.
|
/// `ReadFile` hook. If the file was read into memory, read from that instead.
|
||||||
|
@ -348,9 +398,10 @@ extern "stdcall" fn hook_ReadFile(
|
||||||
if !hFile.is_invalid() {
|
if !hFile.is_invalid() {
|
||||||
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
if let Some((handle, data)) = state.file_by_handle(hFile) {
|
if let Some((handle, data)) = state.file_by_handle(hFile) {
|
||||||
|
let file_size = data.len() as u64 - 1 /* null terminator */;
|
||||||
let count = min(
|
let count = min(
|
||||||
nNumberOfBytesToRead,
|
nNumberOfBytesToRead,
|
||||||
u32::try_from(data.len() as u64 - handle.pos).unwrap_or(u32::MAX),
|
u32::try_from(file_size - handle.pos).unwrap_or(u32::MAX),
|
||||||
);
|
);
|
||||||
unsafe {
|
unsafe {
|
||||||
std::ptr::copy_nonoverlapping(
|
std::ptr::copy_nonoverlapping(
|
||||||
|
@ -364,7 +415,7 @@ extern "stdcall" fn hook_ReadFile(
|
||||||
unsafe { *lpNumberOfBytesRead = count };
|
unsafe { *lpNumberOfBytesRead = count };
|
||||||
}
|
}
|
||||||
debug_println!(
|
debug_println!(
|
||||||
"OVERRIDE: ReadFile({:#X}, {:?}, {:#X}, {:?}) = {:#X}",
|
"OVERRIDE: ReadFile({:p}, {:?}, {:#X}, {:?}) = {:#X}",
|
||||||
hFile.0,
|
hFile.0,
|
||||||
lpBuffer,
|
lpBuffer,
|
||||||
nNumberOfBytesToRead,
|
nNumberOfBytesToRead,
|
||||||
|
@ -378,23 +429,259 @@ extern "stdcall" fn hook_ReadFile(
|
||||||
let ret = unsafe {
|
let ret = unsafe {
|
||||||
ReadFile(
|
ReadFile(
|
||||||
hFile,
|
hFile,
|
||||||
Some(lpBuffer),
|
Some(std::slice::from_raw_parts_mut(
|
||||||
nNumberOfBytesToRead,
|
lpBuffer as *mut u8,
|
||||||
|
nNumberOfBytesToRead as usize,
|
||||||
|
)),
|
||||||
Some(lpNumberOfBytesRead),
|
Some(lpNumberOfBytesRead),
|
||||||
Some(lpOverlapped),
|
Some(lpOverlapped),
|
||||||
)
|
)
|
||||||
};
|
}
|
||||||
|
.is_ok();
|
||||||
debug_println!(
|
debug_println!(
|
||||||
"ReadFile({:#X}, {:?}, {:#X}, {:?}) = {:#X}",
|
"ReadFile({:p}, {:?}, {:#X}, {:?}) = {}",
|
||||||
hFile.0,
|
hFile.0,
|
||||||
lpBuffer,
|
lpBuffer,
|
||||||
nNumberOfBytesToRead,
|
nNumberOfBytesToRead,
|
||||||
lpNumberOfBytesRead,
|
lpNumberOfBytesRead,
|
||||||
|
ret
|
||||||
|
);
|
||||||
|
ret.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `ReadFileEx` hook. Currently unsupported.
|
||||||
|
extern "stdcall" fn hook_ReadFileEx(
|
||||||
|
hFile: HANDLE,
|
||||||
|
lpBuffer: *mut c_void,
|
||||||
|
nNumberOfBytesToRead: u32,
|
||||||
|
lpOverlapped: *mut OVERLAPPED,
|
||||||
|
lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
|
||||||
|
) -> BOOL {
|
||||||
|
if !hFile.is_invalid() {
|
||||||
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
|
if let Some((_handle, _data)) = state.file_by_handle(hFile) {
|
||||||
|
fail!("sjiswrap: ReadFileEx() is not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass through un-encoded files
|
||||||
|
let ret = unsafe {
|
||||||
|
ReadFileEx(
|
||||||
|
hFile,
|
||||||
|
if lpBuffer.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(std::slice::from_raw_parts_mut(
|
||||||
|
lpBuffer as *mut u8,
|
||||||
|
nNumberOfBytesToRead as usize,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
lpOverlapped,
|
||||||
|
lpCompletionRoutine,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.is_ok();
|
||||||
|
debug_println!(
|
||||||
|
"ReadFileEx({:p}, {:?}, {:#X}, {:?}, {:?}) = {}",
|
||||||
|
hFile.0,
|
||||||
|
lpBuffer,
|
||||||
|
nNumberOfBytesToRead,
|
||||||
|
lpOverlapped,
|
||||||
|
lpCompletionRoutine,
|
||||||
|
ret
|
||||||
|
);
|
||||||
|
ret.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `CreateFileMappingA` hook. Currently unsupported.
|
||||||
|
extern "stdcall" fn hook_CreateFileMappingA(
|
||||||
|
hFile: HANDLE,
|
||||||
|
lpAttributes: *const SECURITY_ATTRIBUTES,
|
||||||
|
flProtect: PAGE_PROTECTION_FLAGS,
|
||||||
|
dwMaximumSizeHigh: u32,
|
||||||
|
dwMaximumSizeLow: u32,
|
||||||
|
lpName: PCSTR,
|
||||||
|
) -> HANDLE {
|
||||||
|
let ret = unsafe {
|
||||||
|
CreateFileMappingA(
|
||||||
|
hFile,
|
||||||
|
if lpAttributes.is_null() { None } else { Some(lpAttributes) },
|
||||||
|
flProtect,
|
||||||
|
dwMaximumSizeHigh,
|
||||||
|
dwMaximumSizeLow,
|
||||||
|
lpName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.unwrap_or(INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
if !hFile.is_invalid() && !ret.is_invalid() {
|
||||||
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
|
if let Some((_handle, _data)) = state.file_by_handle(hFile) {
|
||||||
|
if let Some(existing) = state.file_mapping_handles.insert(ret.0, hFile) {
|
||||||
|
fail!(
|
||||||
|
"sjiswrap: CreateFileMappingA({:p}, {:?}, {:#X}, {:#X}, {:#X}, {:?}): Mapping already exists for {:p}",
|
||||||
|
hFile.0,
|
||||||
|
lpAttributes,
|
||||||
|
flProtect.0,
|
||||||
|
dwMaximumSizeHigh,
|
||||||
|
dwMaximumSizeLow,
|
||||||
|
lpName,
|
||||||
|
existing.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
debug_println!(
|
||||||
|
"OVERRIDE CreateFileMappingA({:p}, {:?}, {:#X}, {:#X}, {:#X}, {:?}) = {:p}",
|
||||||
|
hFile.0,
|
||||||
|
lpAttributes,
|
||||||
|
flProtect.0,
|
||||||
|
dwMaximumSizeHigh,
|
||||||
|
dwMaximumSizeLow,
|
||||||
|
lpName,
|
||||||
|
ret.0
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_println!(
|
||||||
|
"CreateFileMappingA({:p}, {:?}, {:#X}, {:#X}, {:#X}, {:?}) = {:p}",
|
||||||
|
hFile.0,
|
||||||
|
lpAttributes,
|
||||||
|
flProtect.0,
|
||||||
|
dwMaximumSizeHigh,
|
||||||
|
dwMaximumSizeLow,
|
||||||
|
lpName,
|
||||||
ret.0
|
ret.0
|
||||||
);
|
);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `CreateFileMappingW` hook. Currently unsupported.
|
||||||
|
extern "stdcall" fn hook_CreateFileMappingW(
|
||||||
|
_hFile: HANDLE,
|
||||||
|
_lpAttributes: *const SECURITY_ATTRIBUTES,
|
||||||
|
_flProtect: u32,
|
||||||
|
_dwMaximumSizeHigh: u32,
|
||||||
|
_dwMaximumSizeLow: u32,
|
||||||
|
_lpName: PCWSTR,
|
||||||
|
) -> HANDLE {
|
||||||
|
fail!("sjiswrap: CreateFileMappingW() is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `OpenFileMappingA` hook. Currently unsupported.
|
||||||
|
extern "stdcall" fn hook_OpenFileMappingA(
|
||||||
|
_dwDesiredAccess: u32,
|
||||||
|
_bInheritHandle: BOOL,
|
||||||
|
_lpName: PCSTR,
|
||||||
|
) -> HANDLE {
|
||||||
|
fail!("sjiswrap: OpenFileMappingA() is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `OpenFileMappingW` hook. Currently unsupported.
|
||||||
|
extern "stdcall" fn hook_OpenFileMappingW(
|
||||||
|
_dwDesiredAccess: u32,
|
||||||
|
_bInheritHandle: BOOL,
|
||||||
|
_lpName: PCWSTR,
|
||||||
|
) -> HANDLE {
|
||||||
|
fail!("sjiswrap: OpenFileMappingW() is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `MapViewOfFile` hook. If the file was read into memory, return that instead.
|
||||||
|
extern "stdcall" fn hook_MapViewOfFile(
|
||||||
|
hFileMappingObject: HANDLE,
|
||||||
|
dwDesiredAccess: FILE_MAP,
|
||||||
|
dwFileOffsetHigh: u32,
|
||||||
|
dwFileOffsetLow: u32,
|
||||||
|
dwNumberOfBytesToMap: usize,
|
||||||
|
) -> *mut c_void {
|
||||||
|
if !hFileMappingObject.is_invalid() {
|
||||||
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
|
if let Some((handle, data)) = state.file_by_mapping_handle(hFileMappingObject) {
|
||||||
|
if dwDesiredAccess.contains(FILE_MAP_WRITE)
|
||||||
|
|| dwDesiredAccess.contains(FILE_MAP_ALL_ACCESS)
|
||||||
|
{
|
||||||
|
fail!("sjiswrap: MapViewOfFile(): Write access to encoded file is not supported");
|
||||||
|
}
|
||||||
|
let offset = (dwFileOffsetHigh as u64) << 32 | dwFileOffsetLow as u64;
|
||||||
|
if offset > 0 {
|
||||||
|
fail!(
|
||||||
|
"sjiswrap: MapViewOfFile({}): Offset is not supported ({})",
|
||||||
|
handle.path.display(),
|
||||||
|
offset
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if dwNumberOfBytesToMap != data.len() - 1
|
||||||
|
/* null terminator */
|
||||||
|
{
|
||||||
|
fail!(
|
||||||
|
"sjiswrap: MapViewOfFile({}): Mapping size mismatch ({} != {})",
|
||||||
|
handle.path.display(),
|
||||||
|
dwNumberOfBytesToMap,
|
||||||
|
data.len() - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let ptr = data.as_ptr() as *mut c_void;
|
||||||
|
debug_println!(
|
||||||
|
"OVERRIDE MapViewOfFile({:p}, {:#X}, {:#X}, {:#X}, {:#X}) = {:p}",
|
||||||
|
hFileMappingObject.0,
|
||||||
|
dwDesiredAccess.0,
|
||||||
|
dwFileOffsetHigh,
|
||||||
|
dwFileOffsetLow,
|
||||||
|
dwNumberOfBytesToMap,
|
||||||
|
ptr
|
||||||
|
);
|
||||||
|
state.view_to_mapping.insert(ptr, hFileMappingObject);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = unsafe {
|
||||||
|
MapViewOfFile(
|
||||||
|
hFileMappingObject,
|
||||||
|
dwDesiredAccess,
|
||||||
|
dwFileOffsetHigh,
|
||||||
|
dwFileOffsetLow,
|
||||||
|
dwNumberOfBytesToMap,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
debug_println!(
|
||||||
|
"MapViewOfFile({:p}, {:#X}, {:#X}, {:#X}, {:#X}) = {:p}",
|
||||||
|
hFileMappingObject.0,
|
||||||
|
dwDesiredAccess.0,
|
||||||
|
dwFileOffsetHigh,
|
||||||
|
dwFileOffsetLow,
|
||||||
|
dwNumberOfBytesToMap,
|
||||||
|
ret.Value
|
||||||
|
);
|
||||||
|
ret.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `MapViewOfFileEx` hook. Currently unsupported.
|
||||||
|
extern "stdcall" fn hook_MapViewOfFileEx(
|
||||||
|
_hFileMappingObject: HANDLE,
|
||||||
|
_dwDesiredAccess: FILE_MAP,
|
||||||
|
_dwFileOffsetHigh: u32,
|
||||||
|
_dwFileOffsetLow: u32,
|
||||||
|
_dwNumberOfBytesToMap: usize,
|
||||||
|
_lpBaseAddress: *mut c_void,
|
||||||
|
) -> *mut c_void {
|
||||||
|
fail!("sjiswrap: MapViewOfFileEx() is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `UnmapViewOfFile` hook. If the file was read into memory, remove the mapping.
|
||||||
|
extern "stdcall" fn hook_UnmapViewOfFile(lpBaseAddress: *mut c_void) -> BOOL {
|
||||||
|
let state = unsafe { GLOBAL_STATE.assume_init_mut() };
|
||||||
|
if let Some(_handle) = state.view_to_mapping.remove(&lpBaseAddress) {
|
||||||
|
debug_println!("OVERRIDE UnmapViewOfFile({:p})", lpBaseAddress);
|
||||||
|
return true.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret =
|
||||||
|
unsafe { UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { Value: lpBaseAddress }) }.is_ok();
|
||||||
|
debug_println!("UnmapViewOfFile({:p}) = {}", lpBaseAddress, ret);
|
||||||
|
ret.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// `SetFilePointer` hook. If the file was read into memory, set the position in that instead.
|
/// `SetFilePointer` hook. If the file was read into memory, set the position in that instead.
|
||||||
extern "stdcall" fn hook_SetFilePointer(
|
extern "stdcall" fn hook_SetFilePointer(
|
||||||
hFile: HANDLE,
|
hFile: HANDLE,
|
||||||
|
@ -408,19 +695,22 @@ extern "stdcall" fn hook_SetFilePointer(
|
||||||
let distance_to_move_high =
|
let distance_to_move_high =
|
||||||
if lpDistanceToMoveHigh.is_null() { 0 } else { unsafe { *lpDistanceToMoveHigh } };
|
if lpDistanceToMoveHigh.is_null() { 0 } else { unsafe { *lpDistanceToMoveHigh } };
|
||||||
let distance_to_move = lDistanceToMove as i64 | (distance_to_move_high as i64) << 32;
|
let distance_to_move = lDistanceToMove as i64 | (distance_to_move_high as i64) << 32;
|
||||||
let file_size = data.len() as u64;
|
let file_size = data.len() as u64 - 1 /* null terminator */;
|
||||||
let pos = min(
|
let pos = min(
|
||||||
match dwMoveMethod {
|
match dwMoveMethod {
|
||||||
FILE_BEGIN => distance_to_move as u64,
|
FILE_BEGIN => distance_to_move as u64,
|
||||||
FILE_CURRENT => handle.pos.saturating_add_signed(distance_to_move),
|
FILE_CURRENT => handle.pos.saturating_add_signed(distance_to_move),
|
||||||
FILE_END => file_size.saturating_add_signed(distance_to_move),
|
FILE_END => file_size.saturating_add_signed(distance_to_move),
|
||||||
_ => panic!("SetFilePointer(): Unsupported move method {:#X}", dwMoveMethod.0),
|
_ => fail!(
|
||||||
|
"sjiswrap: SetFilePointer(): Unsupported move method {:#X}",
|
||||||
|
dwMoveMethod.0
|
||||||
|
),
|
||||||
},
|
},
|
||||||
file_size,
|
file_size,
|
||||||
);
|
);
|
||||||
handle.pos = pos;
|
handle.pos = pos;
|
||||||
debug_println!(
|
debug_println!(
|
||||||
"OVERRIDE SetFilePointer({:#X}, {:#X}, {:?}, {}) = {:#X}",
|
"OVERRIDE SetFilePointer({:p}, {:#X}, {:?}, {}) = {:#X}",
|
||||||
hFile.0,
|
hFile.0,
|
||||||
distance_to_move,
|
distance_to_move,
|
||||||
lpDistanceToMoveHigh,
|
lpDistanceToMoveHigh,
|
||||||
|
@ -437,7 +727,7 @@ extern "stdcall" fn hook_SetFilePointer(
|
||||||
let ret =
|
let ret =
|
||||||
unsafe { SetFilePointer(hFile, lDistanceToMove, Some(lpDistanceToMoveHigh), dwMoveMethod) };
|
unsafe { SetFilePointer(hFile, lDistanceToMove, Some(lpDistanceToMoveHigh), dwMoveMethod) };
|
||||||
debug_println!(
|
debug_println!(
|
||||||
"SetFilePointer({:#X}, {:#X}, {:?}, {}) = {:#X}",
|
"SetFilePointer({:p}, {:#X}, {:?}, {}) = {:#X}",
|
||||||
hFile.0,
|
hFile.0,
|
||||||
lDistanceToMove,
|
lDistanceToMove,
|
||||||
lpDistanceToMoveHigh,
|
lpDistanceToMoveHigh,
|
||||||
|
@ -450,6 +740,120 @@ extern "stdcall" fn hook_SetFilePointer(
|
||||||
/// `IsDBCSLeadByte` hook. This normally uses the system codepage, override with Shift JIS behavior.
|
/// `IsDBCSLeadByte` hook. This normally uses the system codepage, override with Shift JIS behavior.
|
||||||
extern "stdcall" fn hook_IsDBCSLeadByte(TestChar: u8) -> BOOL { (TestChar & 0x80 != 0).into() }
|
extern "stdcall" fn hook_IsDBCSLeadByte(TestChar: u8) -> BOOL { (TestChar & 0x80 != 0).into() }
|
||||||
|
|
||||||
|
fn slice_of<T>(ptr: *const T, len: i32) -> &'static [T]
|
||||||
|
where T: Copy + Zero {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return &[];
|
||||||
|
}
|
||||||
|
if len < 0 {
|
||||||
|
// Null terminated
|
||||||
|
let mut len = 0;
|
||||||
|
while !unsafe { *ptr.offset(len) }.is_zero() {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
unsafe { std::slice::from_raw_parts(ptr, len as usize) }
|
||||||
|
} else {
|
||||||
|
unsafe { std::slice::from_raw_parts(ptr, len as usize) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slice_of_mut<T>(ptr: *mut T, len: i32) -> Option<&'static mut [T]>
|
||||||
|
where T: Copy + Zero {
|
||||||
|
if ptr.is_null() || len < 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(unsafe { std::slice::from_raw_parts_mut(ptr, len as usize) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `MultiByteToWideChar` hook. This reimplements the conversion for Shift JIS, using the pre-XP
|
||||||
|
/// behavior of failing on illegal code points. MWCC 3.0 relies on this behavior.
|
||||||
|
/// See https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
|
||||||
|
extern "stdcall" fn hook_MultiByteToWideChar(
|
||||||
|
CodePage: u32,
|
||||||
|
dwFlags: MULTI_BYTE_TO_WIDE_CHAR_FLAGS,
|
||||||
|
lpMultiByteStr: PCSTR,
|
||||||
|
cbMultiByte: i32,
|
||||||
|
lpWideCharStr: *mut u16,
|
||||||
|
cchWideChar: i32,
|
||||||
|
) -> i32 {
|
||||||
|
let mb_str = slice_of(lpMultiByteStr.as_ptr(), cbMultiByte);
|
||||||
|
let mut wide_str = slice_of_mut(lpWideCharStr, cchWideChar);
|
||||||
|
let decoder = match CodePage {
|
||||||
|
0 => UTF_8,
|
||||||
|
932 => SHIFT_JIS,
|
||||||
|
_ => {
|
||||||
|
// Try to pass through
|
||||||
|
let ret =
|
||||||
|
unsafe { MultiByteToWideChar(CodePage, dwFlags, mb_str, wide_str.as_deref_mut()) };
|
||||||
|
debug_println!(
|
||||||
|
"MultiByteToWideChar({}, {:#X}, {:?} ({:X?}), {:?}) = {} ({:?})",
|
||||||
|
CodePage,
|
||||||
|
dwFlags.0,
|
||||||
|
mb_str,
|
||||||
|
lpMultiByteStr,
|
||||||
|
wide_str,
|
||||||
|
ret,
|
||||||
|
unsafe { GetLastError() }
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (decoded, _, err) = decoder.decode(mb_str);
|
||||||
|
let ret = if err {
|
||||||
|
unsafe { SetLastError(ERROR_NO_UNICODE_TRANSLATION) };
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
match wide_str.as_deref_mut() {
|
||||||
|
None => {
|
||||||
|
unsafe { SetLastError(ERROR_SUCCESS) };
|
||||||
|
decoded.encode_utf16().count() as i32
|
||||||
|
}
|
||||||
|
Some(out) => {
|
||||||
|
let mut out = out.iter_mut();
|
||||||
|
let mut written = 0;
|
||||||
|
for mut c in decoded.encode_utf16() {
|
||||||
|
if c == 0xFFFD && CodePage == 932 {
|
||||||
|
// CP-932 replacement character
|
||||||
|
c = 0x30FB;
|
||||||
|
}
|
||||||
|
if let Some(out) = out.next() {
|
||||||
|
*out = c;
|
||||||
|
written += 1;
|
||||||
|
} else {
|
||||||
|
// Insufficient buffer
|
||||||
|
written = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if written < 0 {
|
||||||
|
unsafe { SetLastError(ERROR_INSUFFICIENT_BUFFER) };
|
||||||
|
decoded.encode_utf16().count() as i32
|
||||||
|
} else {
|
||||||
|
unsafe { SetLastError(ERROR_SUCCESS) };
|
||||||
|
written
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug_println!(
|
||||||
|
"OVERRIDE MultiByteToWideChar({}, {:#X}, {:?} ({:X?}), {:?}) = {} ({:?})",
|
||||||
|
CodePage,
|
||||||
|
dwFlags.0,
|
||||||
|
decoded,
|
||||||
|
mb_str,
|
||||||
|
wide_str,
|
||||||
|
ret,
|
||||||
|
unsafe { GetLastError() }
|
||||||
|
);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `GetACP` hook. Return the Shift JIS codepage.
|
||||||
|
extern "stdcall" fn hook_GetACP() -> u32 {
|
||||||
|
debug_println!("OVERRIDE GetACP() = 932");
|
||||||
|
932
|
||||||
|
}
|
||||||
|
|
||||||
/// `GetModuleFileNameA` hook. Return the absolute path of the executable.
|
/// `GetModuleFileNameA` hook. Return the absolute path of the executable.
|
||||||
extern "stdcall" fn hook_GetModuleFileNameA(
|
extern "stdcall" fn hook_GetModuleFileNameA(
|
||||||
hModule: HMODULE,
|
hModule: HMODULE,
|
||||||
|
@ -472,7 +876,7 @@ extern "stdcall" fn hook_GetModuleFileNameA(
|
||||||
let slice = unsafe { std::slice::from_raw_parts(lpFilename as *const u8, ret as usize) };
|
let slice = unsafe { std::slice::from_raw_parts(lpFilename as *const u8, ret as usize) };
|
||||||
let str = unsafe { std::str::from_utf8_unchecked(slice) };
|
let str = unsafe { std::str::from_utf8_unchecked(slice) };
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"OVERRIDE GetModuleFileNameA({:#X}, {:?}, {:#X}) = {:#X} ({})",
|
"OVERRIDE GetModuleFileNameA({:p}, {:?}, {:#X}) = {:#X} ({})",
|
||||||
hModule.0, lpFilename, nSize, ret, str
|
hModule.0, lpFilename, nSize, ret, str
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue