├── .github └── FUNDING.yml ├── Makefile ├── ap-type-checker.bat ├── ap-type-checker.sh ├── aprip.c ├── build.md ├── cd-command-logger.bat ├── cd-command-logger.sh ├── changelog.md ├── control-i386 ├── control-x86_64 ├── icepick-patch-re.txt ├── images ├── ap-type-checker-biohazard3jdemo.png ├── ap-type-checker-biohazard3jr0.png ├── ap-type-checker-dino-crisis-usa-rev-0.png ├── ap-type-checker-poporogue-japan-rev-0-dump-ram.png ├── ap-type-checker-poporogue-japan-rev-0.png ├── ap-type-checker-resident-evil-3-germany.png ├── ap-type-checker-resident-evil-3-japan-demo.png ├── ap-type-checker-resident-evil-3-japan.png ├── ape-escape-europe-disc-image-patched.png ├── dino-crisis-2-japan-disc-image-patched.png ├── dino-crisis-europe-lc2-patched.png ├── dino-crisis-usa-rev-0-code-generation.png ├── dino-crisis-usa-rev-0-non-stealth-mod-chip-apv2-triggered.png ├── dino-crisis-usa-rev-0-ram-dump.png ├── dinocrisis2j-patched.png ├── duckstation-bios-settings.png ├── duckstation-changed-settings.png ├── duckstation-confirm-no-sbi.png ├── duckstation-default-settings.png ├── duckstation-open-settings.png ├── duckstation-ram-settings.png ├── duckstation-save-state-setting.png ├── medievil-europe-lc1-screen.png.png ├── medievil-france-disc-image-patched.png ├── parasite-eve-2-europe-disc-1-cd-image-patched.png ├── poporogue-japan-rev-0-non-stealth-mod-chip-apv1-triggered.png ├── resident-evil-3-germany-code-generation.png ├── resident-evil-3-germany-dump-ram.png ├── resident-evil-3-japan-demo-dump-ram.png ├── resident-evil-3-japan-dump-ram.png ├── resident-evil-3-japan-rev-0-dump-ram.png └── resident-evil-3-japan-to-japan-demo-conversion.png ├── license.md ├── readme.md └── variables.mk /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [alex-free] 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # EzRe v1.0 GNUMakefile for Linux/Windows by Alex Free 2 | 3 | include variables.mk 4 | 5 | COMPILER_FLAGS+=-DVERSION=\"$(VERSION)\" 6 | 7 | $(PROGRAM): clean 8 | mkdir -p $(BUILD_DIR) 9 | ifeq ($(strip $(EXECUTABLE_NAME)),) 10 | $(COMPILER) $(COMPILER_FLAGS) $(SOURCE_FILES) -o $(BUILD_DIR)/$(PROGRAM) 11 | else 12 | $(COMPILER) $(COMPILER_FLAGS) $(SOURCE_FILES) -o $(BUILD_DIR)/$(EXECUTABLE_NAME) 13 | endif 14 | 15 | .PHONY: deps-apt 16 | deps-apt: 17 | sudo apt update --yes 18 | sudo apt install --yes $(BUILD_DEPENDS_APT) 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -rf $(BUILD_DIR)/$(PROGRAM).exe $(BUILD_DIR)/$(PROGRAM) 23 | 24 | .PHONY: clean-build 25 | clean-build: 26 | rm -rf $(BUILD_DIR) 27 | 28 | .PHONY: linux-i386 29 | linux-i386: clean 30 | make $(PROGRAM) COMPILER_FLAGS='$(COMPILER_FLAGS_LINUX_X86) $(COMPILER_FLAGS)' EXECUTABLE_NAME='$(PROGRAM).i386' 31 | 32 | .PHONY: linux-x86_64 33 | linux-x86_64: clean 34 | make $(PROGRAM) EXECUTABLE_NAME='$(PROGRAM).x86_64' 35 | 36 | .PHONY: windows-i686 37 | windows-i686: clean 38 | make $(PROGRAM) COMPILER=$(WINDOWS_X86_COMPILER) EXECUTABLE_NAME='$(PROGRAM).i686.exe' 39 | 40 | .PHONY: windows-x86_64 41 | windows-x86_64: clean 42 | make $(PROGRAM) COMPILER=$(WINDOWS_X86_64_COMPILER) EXECUTABLE_NAME='$(PROGRAM).x86_64.exe' 43 | 44 | .PHONY: release 45 | release: 46 | rm -rf $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) $(BUILD_DIR)/$(PROGRAM)-$(VERSION)-$(PLATFORM).zip 47 | mkdir $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) 48 | ifeq ($(strip $(WINDOWS_RELEASE)),) 49 | cp $(BUILD_DIR)/$(EXECUTABLE_NAME) $(BUILD_DIR)/$(PROGRAM) 50 | cp -r $(BUILD_DIR)/$(PROGRAM) $(RELEASE_FILES) $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) 51 | else 52 | cp $(BUILD_DIR)/$(EXECUTABLE_NAME) $(BUILD_DIR)/$(PROGRAM).exe 53 | cp -r $(BUILD_DIR)/$(PROGRAM).exe $(RELEASE_FILES) $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) 54 | endif 55 | chmod -R 777 $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) 56 | cd $(BUILD_DIR) && zip -rq $(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM).zip $(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) 57 | rm -rf $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(PLATFORM) 58 | 59 | .PHONY: linux-i386-release 60 | linux-i386-release: linux-i386 61 | ifeq ($(strip $(LINUX_SPECIFIC_RELEASE_FILES)),) 62 | make release PLATFORM='$(LINUX_I386_RELEASE_NAME_SUFFIX)' EXECUTABLE_NAME='$(PROGRAM).i386' 63 | else 64 | make release PLATFORM='$(LINUX_I386_RELEASE_NAME_SUFFIX)' RELEASE_FILES='$(LINUX_SPECIFIC_RELEASE_FILES) $(RELEASE_FILES)' EXECUTABLE_NAME='$(PROGRAM).i386' 65 | endif 66 | 67 | .PHONY: linux-x86_64-release 68 | linux-x86_64-release: linux-x86_64 69 | ifeq ($(strip $(LINUX_SPECIFIC_RELEASE_FILES)),) 70 | make release PLATFORM='$(LINUX_X86_64_RELEASE_NAME_SUFFIX)' EXECUTABLE_NAME='$(PROGRAM).x86_64' 71 | else 72 | make release PLATFORM='$(LINUX_X86_64_RELEASE_NAME_SUFFIX)' RELEASE_FILES='$(LINUX_SPECIFIC_RELEASE_FILES) $(RELEASE_FILES)' EXECUTABLE_NAME='$(PROGRAM).x86_64' 73 | endif 74 | 75 | .PHONY: windows-i686-release 76 | windows-i686-release: windows-i686 77 | ifeq ($(strip $(WINDOWS_SPECIFIC_RELEASE_FILES)),) 78 | make release PLATFORM='$(WINDOWS_I686_RELEASE_NAME_SUFFIX)' EXECUTABLE_NAME='$(PROGRAM).i686.exe' WINDOWS_RELEASE=true 79 | else 80 | make release PLATFORM='$(WINDOWS_I686_RELEASE_NAME_SUFFIX)' RELEASE_FILES='$(WINDOWS_SPECIFIC_RELEASE_FILES) $(RELEASE_FILES)' EXECUTABLE_NAME='$(PROGRAM).i686.exe' WINDOWS_RELEASE=true 81 | endif 82 | 83 | .PHONY: windows-x86_64-release 84 | windows-x86_64-release: windows-x86_64 85 | ifeq ($(strip $(WINDOWS_SPECIFIC_RELEASE_FILES)),) 86 | make release PLATFORM='$(WINDOWS_X86_64_RELEASE_NAME_SUFFIX)' EXECUTABLE_NAME='$(PROGRAM).x86_64.exe' WINDOWS_RELEASE=true 87 | else 88 | make release PLATFORM='$(WINDOWS_X86_64_RELEASE_NAME_SUFFIX)' RELEASE_FILES='$(WINDOWS_SPECIFIC_RELEASE_FILES) $(RELEASE_FILES)' EXECUTABLE_NAME='$(PROGRAM).x86_64.exe' WINDOWS_RELEASE=true 89 | endif 90 | 91 | .PHONY: linux-i386-deb 92 | linux-i386-deb: linux-i386 93 | rm -rf $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX) 94 | mkdir -p $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX)/usr/bin 95 | mkdir -p $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX)/DEBIAN 96 | cp $(BUILD_DIR)/$(EXECUTABLE_NAME) $(BUILD_DIR)/$(PROGRAM) 97 | cp -r $(BUILD_DIR)/$(PROGRAM) $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX)/usr/bin 98 | cp control-i386 $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX)/DEBIAN/control 99 | dpkg-deb --build $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX) 100 | rm -rf $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_I386_RELEASE_NAME_SUFFIX) 101 | 102 | .PHONY: linux-x86_64-deb 103 | linux-x86_64-deb: linux-x86_64 104 | rm -rf $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX) 105 | mkdir -p $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX)/usr/bin 106 | mkdir -p $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX)/DEBIAN 107 | cp $(BUILD_DIR)/$(EXECUTABLE_NAME) $(BUILD_DIR)/$(PROGRAM) 108 | cp -r $(BUILD_DIR)/$(PROGRAM) $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX)/usr/bin 109 | cp control-x86_64 $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX)/DEBIAN/control 110 | dpkg-deb --build $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX) 111 | rm -rf $(BUILD_DIR)/$(RELEASE_BASE_NAME)-$(VERSION)-$(LINUX_X86_64_RELEASE_NAME_SUFFIX) 112 | 113 | .PHONY: all 114 | all: 115 | make clean-build 116 | 117 | make linux-i386-release 118 | make linux-i386-deb EXECUTABLE_NAME='$(PROGRAM).i386' 119 | 120 | make linux-x86_64-release 121 | make linux-x86_64-deb EXECUTABLE_NAME='$(PROGRAM).x86_64' 122 | 123 | make windows-i686-release 124 | make windows-x86_64-release 125 | 126 | make clean 127 | -------------------------------------------------------------------------------- /ap-type-checker.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo DuckStation AP Type Checker v1.0.1 For Windows 4 | echo By Alex Free 5 | set argC=0 6 | for %%x in (%*) do Set /A argC+=1 7 | 8 | IF NOT "%argC%" == "2" ( 9 | echo Error: Incorrect number of arguments given to %0%. 10 | echo Usage: 11 | echo %0% ^ ^ 12 | echo. 13 | cmd /k 14 | ) 15 | 16 | IF NOT EXIST "%~f1" ( 17 | echo Error: Can't open the DuckStation executable file: "%~f1" 18 | echo. 19 | cmd /k 20 | ) 21 | 22 | IF NOT EXIST "%~f2" ( 23 | echo Error: Can't open the PSX game cue file: "%~f2" 24 | echo. 25 | cmd /k 26 | ) 27 | 28 | FOR /F "tokens=3 delims= " %%G IN ('REG QUERY "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal"') DO (SET docsdir=%%G) 29 | set log="%docsdir%\DuckStation\duckstation.log" 30 | del %log% 2> nul 31 | "%~f1" "%~f2" 32 | echo. 33 | echo Number Of 0x19 Test Commands Sent: 34 | %SystemRoot%\system32\find /C "CDROM executing command 0x19" %log% 35 | echo. 36 | %SystemRoot%\system32\find "CDROM executing command 0x19" %log% 37 | echo. 38 | echo Number Of 0x1E ReadTOC Commands Sent: 39 | %SystemRoot%\system32\find /C "CDROM executing command 0x1E" %log% 40 | echo. 41 | %SystemRoot%\system32\find "CDROM executing command 0x1E" %log% 42 | echo Number Of 0x13 GetTN Commands Sent: 43 | %SystemRoot%\system32\find /C "CDROM executing command 0x13" %log% 44 | echo. 45 | %SystemRoot%\system32\find "CDROM executing command 0x13" %log% 46 | echo. 47 | echo Number Of 0x14 GetTD Commands Sent: 48 | %SystemRoot%\system32\find /C "CDROM executing command 0x14" %log% 49 | echo. 50 | %SystemRoot%\system32\find "CDROM executing command 0x14" %log% -------------------------------------------------------------------------------- /ap-type-checker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "DuckStation AP Type Checker v1.0.1 For Linux\nBy Alex Free\n" 4 | 5 | if [ $# -ne 2 ]; then 6 | echo -e "Error: Incorrect number of arguments.\nUsage:\n \n" 7 | exit 1 8 | fi 9 | 10 | if [ ! -f "$1" ]; then 11 | echo -e "Error: Can't open the DuckStation executable file: "$1"\n" 12 | exit 1 13 | fi 14 | 15 | if [ ! -f "$2" ]; then 16 | echo -e "Error: Can't open the PSX game cue file: "$2"\n" 17 | exit 1 18 | fi 19 | 20 | log="$HOME"/.local/share/duckstation/duckstation.log 21 | rm -f "$log" 22 | "$1" "$2" 23 | 24 | 25 | echo "Number Of 0x19 Test Commands Sent:" 26 | grep -rc "CDROM executing command 0x19" "$log" 27 | echo 28 | grep -r "CDROM executing command 0x19" "$log" 29 | echo 30 | echo "Number Of 0x1E ReadTOC Commands Sent:" 31 | grep -rc "CDROM executing command 0x1E" "$log" 32 | echo 33 | grep -r "CDROM executing command 0x1E" "$log" 34 | echo 35 | echo "Number Of 0x13 GetTN Commands Sent:" 36 | grep -rc "CDROM executing command 0x13" "$log" 37 | echo 38 | grep -r "CDROM executing command 0x13" "$log" 39 | echo 40 | echo "Number Of 0x14 GetTD Commands Sent:" 41 | grep -rc "CDROM executing command 0x14" "$log" 42 | echo 43 | grep -r "CDROM executing command 0x14" "$log" 44 | -------------------------------------------------------------------------------- /aprip.c: -------------------------------------------------------------------------------- 1 | /* 2 | BSD 3-Clause License 3 | Copyright (c) 2022-2024, Alex Free 4 | All rights reserved. 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of the copyright holder nor the names of its 13 | contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | FILE *bin; 33 | FILE *bin_2; 34 | FILE *mem_dump_1; 35 | FILE *mem_dump_2; 36 | FILE *patch; 37 | 38 | unsigned int bin_size; 39 | unsigned int bin_size_2; 40 | unsigned int lba; 41 | unsigned int max_size; 42 | unsigned int mem_dump_1_size; 43 | unsigned int mem_dump_2_size; 44 | unsigned int current_fpos; 45 | unsigned int number_of_sectors; 46 | unsigned int number_of_sectors_2; 47 | unsigned int search_size; 48 | unsigned int valid_mem_dump_size = 0x200000; // The exact file size generated when dumping RAM in the DuckStation emulator. 49 | unsigned int gameshark_write_byte_address; 50 | unsigned int get_start_of_pattern_pos; 51 | unsigned int magic_word; 52 | 53 | unsigned char *buf; 54 | unsigned char *buf_2; 55 | unsigned char *bytes; 56 | unsigned char *mem_dump_1_buf; 57 | unsigned char *mem_dump_2_buf; 58 | 59 | unsigned char last_byte; 60 | unsigned char gameshark_prefix; 61 | unsigned char sectors[0x1000]; 62 | unsigned char sectors_2[0x1000]; 63 | 64 | const unsigned char sync = 0x00; 65 | 66 | bool find_first_different_byte = true; 67 | bool libcrypt = false; 68 | 69 | const unsigned char anti_piracy_v1[] = { // The very first anti-piracy code. Found in PopoRogue, Ape Escape, etc. Does only 3 SCEX check for dumb non-stealth modchips. 70 | 0x01, 0x00, 0x01, 0x03, // GetStat 71 | 0x07, 0x00, 0x01, 0x03, // Motor on 72 | 0x02, 0x03, 0x01, 0x03, // SetLoc 73 | 0x16, 0x00, 0x01, 0x03, // SeekAudio 74 | 0x0E, 0x01, 0x01, 0x03, // SetMode 75 | 0x19, 0x01, 0x04, 0x03, // Subfunq X (19'04) //21st 76 | 0x0B, 0x00, 0x01, 0x03, // Mute 77 | 0x03, 0x00, 0x01, 0x03, // Play 78 | 0x19, 0x01, 0x02, 0x03, // Subfunq Y (19'05) //33rd 79 | 0x09, 0x00, 0x01, 0x03 // Pause 80 | }; // 40 byte pattern 81 | 82 | const unsigned char medievil_europe_ps_exe [] = { 0x53, 0x43, 0x45, 0x53, 0x5F, 0x30, 0x30, 0x33, 0x2E, 0x31, 0x31, 0x3B, 0x31 }; // SCES_003.11;1 83 | const unsigned char medievil_france_ps_exe [] = { 0x53, 0x43, 0x45, 0x53, 0x5F, 0x30, 0x31, 0x34, 0x2E, 0x39, 0x32, 0x3B, 0x31 }; // SCES_014.92;1 84 | const unsigned char medievil_germany_ps_exe [] = { 0x53, 0x43, 0x45, 0x53, 0x5F, 0x30, 0x31, 0x34, 0x2E, 0x39, 0x33, 0x3B, 0x31 }; // SCES_014.93;1 85 | const unsigned char medievil_italian_ps_exe [] = { 0x53, 0x43, 0x45, 0x53, 0x5F, 0x30, 0x31, 0x34, 0x2E, 0x39, 0x34, 0x3B, 0x31 }; // SCES_014.94;1 86 | const unsigned char medievil_spain_ps_exe [] = { 0x53, 0x43, 0x45, 0x53, 0x5F, 0x30, 0x31, 0x34, 0x2E, 0x39, 0x35, 0x3B, 0x31 }; // SCES_014.95;1 87 | 88 | const unsigned char libcrypt_2_anti_pro_action_replay[] = { 89 | 0x80, 0xE1, 90 | 0x02, 0x3C, 91 | 0x00, 0x38, 92 | 0x82, 0x40 93 | }; 94 | 95 | const unsigned char libcrypt_2_anti_mod_chip[] = { 96 | 0x08, 0x00, 97 | 0x20, 0x14, 98 | 0x02, 0x00, // not modified 99 | 0xE7, 0x30, // not modified 100 | 0x06, 0x00, 101 | 0xE0, 0x10, 102 | 0xAD, 0xFF, // not modified 103 | 0x84, 0x20, // not modified 104 | 0x04, 0x00, 105 | 0x80, 0x14, 106 | 0x00, 0x00, // not modified 107 | 0x00, 0x00 // not modified 108 | }; 109 | 110 | const unsigned char libcrypt_2_magic_word[] = { 111 | 0x25, 0x30, 112 | 0x86, 0x00 113 | }; 114 | 115 | // ICEPICK patch from TRSIMEDI, modified for aprip 116 | const unsigned char libcrypt_1_medievil_icepick_based_patch[] = { 117 | 0x0A, // 0 118 | 0x00, // not a value checked // 1 119 | 0x80, // 2 120 | 0x14, // 3 121 | 0x00, // not a value checked // 4 122 | 0x00, // not a value checked // 5 123 | 0x00, // not a value checked // 6 124 | 0x00, // not a value checked // 7 125 | 0x00, // not a value checked // 8 126 | 0x00, // not a value checked // 9 127 | 0xA3, // 10 128 | 0x90, // 11 129 | 0x02, // 12 130 | 0x00, // not a value checked // 13 131 | 0x02, // 14 132 | 0x24, // 15 133 | 0x06, // 16 134 | 0x00, // not a value checked // 17 135 | 0x62, // 18 136 | 0x14, // 19 137 | 0x0E, // 20 138 | 0x80, // 21 139 | 0x03, // 22 140 | 0x3C, // 23 141 | 0x04, // 24 142 | 0x00, // not a value checked // 25 143 | 0xA3, // 26 144 | 0x90, // 27 145 | 0x53, // 28 146 | 0x00, // not a value checked // 29 147 | 0x02, // 30 148 | 0x24, // 31 149 | 0x02, // 32 150 | 0x00, // not a value checked // 33 151 | 0x62, // 34 152 | 0x14, // 35 153 | 0x0E, // 36 154 | 0x80, // 37 155 | 0x03, // 38 156 | 0x3C, // 39 157 | 0x07, // 40 158 | 0x00, // not a value checked // 41 159 | 0xA4, // 42 160 | 0x90, // 43 161 | 0x00, // byte 2 of MW // 44 162 | 0x00, // byte 1 of MW // 45 163 | 0x00, // 46 164 | 0x00 // 47 165 | }; // 0x2F, 47 bytes 166 | 167 | const unsigned char anti_piracy_v2_vc0_bypass[] = { // This was first seen in Dino Crisis? Does SCEX/GetTN/GetTD/ReadTOC but is standardized and very easy to bypass. Seems like all games after a certain point began copying in this code to add anti-piracy measures to their product. 168 | 0x01, 0x00, 0x01, 0x03, // GetStat 169 | 0x13, 0x00, 0x03, 0x03, // GetTN 170 | 0x14, 0x01, 0x03, 0x03, // GetTD 171 | 0x02, 0x03, 0x01, 0x03, // SetLoc 172 | 0x16, 0x00, 0x01, 0x05, // SeekAudio 173 | 0x0E, 0x01, 0x01, 0x03, // SetMode 174 | 0x0A, 0x00, 0x01, 0x05, // Init 175 | 0x0B, 0x00, 0x01, 0x03, // Mute 176 | 0x03, 0x00, 0x01, 0x03, // Play 177 | 0x19, 0x01, 0x01, 0x03, // Subfunq X (19'04) //37th 178 | 0x19, 0x01, 0x02, 0x03, // Subfunq Y (19'05) //41st 179 | 0x09, 0x00, 0x01, 0x05, // Pause 180 | 0x1E, 0x00, 0x01, 0x05, // ReadTOC //49th 181 | 0x1A, 0x00, 0x01, 0x05 // GetID 182 | }; //52 byte pattern 183 | 184 | const unsigned char anti_piracy_v2_pal_bypass[] = { // 16 byte pattern 185 | 0xC8, 0xBF, 0x03, 0x3C, 186 | 0x52, 0xFF, 0x63, 0x90, 187 | 0x45, 0x00, 0x02, 0x24, 188 | 0x0A, 0x00, 0x62, 0x10 // change to 0x0A, 0x00, 0x00, 0x18 to make the game think your using a PAL BIOS/console 189 | }; 190 | 191 | const unsigned char beatmania_append_gotta_mix_system_cnf[] = { // 66 byte pattern 192 | 0x42, 0x4F, 0x4F, 0x54, 193 | 0x20, 0x3D, 0x20, 0x63, 194 | 0x64, 0x72, 0x6F, 0x6D, 195 | 0x3A, 0x5C, 0x53, 0x4C, 196 | 0x50, 0x4D, 0x5F, 0x38, 197 | 0x36, 0x32, 0x2E, 0x32, 198 | 0x39, 0x3B, 0x31, 0x0D, 199 | 0x0A, 0x54, 0x43, 0x42, 200 | 0x20, 0x3D, 0x20, 0x34, 201 | 0x0D, 0x0A, 0x45, 0x56, 202 | 0x45, 0x4E, 0x54, 0x20, 203 | 0x3D, 0x20, 0x31, 0x30, 204 | 0x0D, 0x0A, 0x53, 0x54, 205 | 0x41, 0x43, 0x4B, 0x20, 206 | 0x3D, 0x20, 0x38, 0x30, 207 | 0x31, 0x46, 0x46, 0x46, 208 | 0x30, 0x30 209 | }; 210 | /* 211 | BOOT = cdrom:\SLPM_866.92;1 212 | TCB = 4 213 | EVENT = 10 214 | STACK = 801FFF00 215 | */ 216 | 217 | const unsigned char beatmania_append_3rd_mix_system_cnf[] = { // 66 byte pattern 218 | 0x42, 0x4F, 0x4F, 0x54, 219 | 0x20, 0x3D, 0x20, 0x63, 220 | 0x64, 0x72, 0x6F, 0x6D, 221 | 0x3A, 0x5C, 0x53, 0x4C, 222 | 0x50, 0x4D, 0x5F, 0x38, 223 | 0x36, 0x31, 0x2E, 0x38, 224 | 0x34, 0x3B, 0x31, 0x0D, 225 | 0x0A, 0x54, 0x43, 0x42, 226 | 0x20, 0x3D, 0x20, 0x34, 227 | 0x0D, 0x0A, 0x45, 0x56, 228 | 0x45, 0x4E, 0x54, 0x20, 229 | 0x3D, 0x20, 0x31, 0x30, 230 | 0x0D, 0x0A, 0x53, 0x54, 231 | 0x41, 0x43, 0x4B, 0x20, 232 | 0x3D, 0x20, 0x38, 0x30, 233 | 0x31, 0x46, 0x46, 0x46, 234 | 0x30, 0x30 235 | }; 236 | /* 237 | BOOT = cdrom:\SLPM_861.84;1 238 | TCB = 4 239 | EVENT = 10 240 | STACK = 801FFF00 241 | */ 242 | 243 | const unsigned char append_exe_name[] = { 0x5C, 0x41, 0x50, 0x50, 0x45, 0x4E, 0x44, 0x2E, 0x45, 0x58, 0x45, }; // \APPEND.EXE 244 | 245 | bool matched_anti_piracy_v1; 246 | bool matched_anti_piracy_v2_pal_bypass; 247 | 248 | bool matched_anti_piracy_v2_vc0_bypass; 249 | 250 | bool last_sector; 251 | 252 | bool matched_libcrypt_2_anti_pro_action_replay; 253 | bool matched_libcrypt_2_anti_mod_chip; 254 | bool matched_libcrypt_2_magic_word; 255 | bool matched_libcrypt_2; 256 | 257 | bool directory_record_sectors_maybe = true; 258 | bool libcrypt_1; 259 | bool libcrypt_2; 260 | bool matched_libcrypt_1_icepick_based_patch; 261 | bool matched_libcrypt_1_magic_word; 262 | bool matched_libcrypt_1_part_2; 263 | bool matched_libcrypt_1_part_3; 264 | 265 | void bin_patch_custom(const char **argv) 266 | { 267 | /* 268 | The patch could possibly start on the end of a sector and end at the beginning of the next sector. Each RAW sector is 0x930 bytes. The first 0x18 bytes are to be ignored as they are just header data. The next 0x800 bytes contains actual data we want to scan through. 269 | Start at 0. Skip to 0x18. Read the next 0x800 bytes. Skip to a total of 0x930 bytes (one whole raw sector). Skip 0x18 bytes again and then read the next 0x800 bytes. We now have 2 sectors worth of straight up data in a buffer of 0x1000 bytes 270 | Run search functions on the 0x1000 byte sized buffer. 271 | */ 272 | fseek(bin, 0, SEEK_END); 273 | bin_size = ftell(bin); 274 | 275 | if(bin_size > 0x2EE00000) // 750MB max, no PSX software comes even close to such a size 276 | { 277 | printf("Error: The CD image BIN file %s exceeds the maximum filesize of 750MB\n", argv[2]); 278 | fclose(bin); 279 | return; 280 | } 281 | 282 | fseek(bin, 0, SEEK_SET); 283 | buf = (unsigned char *)malloc(bin_size * sizeof(unsigned char)); // Read entire BIN to memory for performance gain, I mean it's 2022 who doesn't have a free ~700MBs of RAM?! 284 | 285 | if(fread(buf, 1, bin_size, bin) != bin_size) 286 | { 287 | printf("Error loading CD image BIN file: %s\n", argv[2]); 288 | return; 289 | } 290 | 291 | printf("Successfully loaded CD image BIN file: %s (%d bytes in memory)\n", argv[2], bin_size); 292 | max_size = bin_size; 293 | number_of_sectors = (bin_size / 2352); 294 | 295 | int line_count = 0; 296 | char ch; 297 | fseek(patch, 0, SEEK_SET); 298 | 299 | // Count the number of newline characters in the file 300 | while ((ch = fgetc(patch)) != EOF) { 301 | if (ch == '\n') { 302 | line_count++; 303 | } 304 | } 305 | 306 | // line count is equal to one byte change, so if it exceeds 0x800 * 2 = 0x10000 then it will segfault 307 | if(line_count > 0x1000) 308 | { 309 | printf("\nGenerated patch is too large, it spans over 2 sectors (MAX is 0x1000 byte pattern/4096 lines). Please try splitting up the patch into seperate 2-sector length max patches.\n"); 310 | fclose(bin); 311 | fclose(patch); 312 | return; 313 | } else { 314 | printf("\nPatch size verified. Line count: %d\n", line_count); 315 | } 316 | fseek(patch, 0, SEEK_SET); 317 | unsigned char custom_patch_before[line_count]; 318 | unsigned char custom_patch_after[line_count]; 319 | bool custom_patch_match; 320 | int i = 0; 321 | while(fscanf(patch, "%hhX %hhX%*[^\n]", &custom_patch_before[i], &custom_patch_after[i]) == 2) 322 | { 323 | printf("%hhX %hhX\n", custom_patch_before[i], custom_patch_after[i]); 324 | i++; 325 | } 326 | 327 | printf("Scanning %d sectors, please wait...\n", number_of_sectors); 328 | 329 | while(1) 330 | { 331 | if(current_fpos > max_size) 332 | break; // even number of sectors, done reading the file. 333 | 334 | if((current_fpos + 0x930) == max_size) // odd number of sectors 335 | last_sector = 1; // This function is reading 2 sectors at a time, so if there is an odd number of sectors we have to change the behavior to only search the last sector. Explicitly break loop when this is set. 336 | 337 | for(int i=0; i < 0x800; i++) 338 | { 339 | sectors[i] = buf[current_fpos + i + 0x18]; // skip 0x18 header info per sector 340 | } 341 | 342 | if(!last_sector) 343 | { 344 | for(int i=0; i < 0x800; i++) 345 | { 346 | sectors[i + 0x800] = buf[current_fpos + i + 0x18 + 0x930]; // skip 0x18 header info then skip exactly 1 sector. Read the next 0x800 bytes. We now have an array's worth of data from 2 sectors which excludes EDC/Header data at the beginning and end of each. 347 | } 348 | search_size = 0x1000; 349 | } else { 350 | search_size = 0x800; 351 | } 352 | 353 | for(int s = 0; s < search_size; s++) 354 | { 355 | 356 | custom_patch_match = true; 357 | for(int i=0; i < line_count; i++) 358 | { 359 | if((custom_patch_before[i] != sectors[s + i]) && (custom_patch_before[i] + custom_patch_after[i] != 0)) 360 | { 361 | custom_patch_match = false; 362 | } 363 | } 364 | 365 | if(custom_patch_match) 366 | { 367 | if(s < 0x800) 368 | { 369 | lba = ((current_fpos / 0x930) + 150); 370 | } else { 371 | lba = ((current_fpos / 0x930) + 151); 372 | } 373 | printf("Got custom match at LBA: %u\n", lba); 374 | 375 | for(int i = 0; i < line_count; i++) 376 | { 377 | if(custom_patch_before[i] + custom_patch_after[i] != 0) 378 | { 379 | printf("Writing %hhX\n", custom_patch_after[i]); 380 | sectors[s + i] = custom_patch_after[i]; 381 | } 382 | } 383 | } 384 | } 385 | 386 | for(int i=0; i < 0x800; i++) 387 | { 388 | buf[current_fpos + i + 0x18] = sectors[i]; // skip 0x18 header info per sector 389 | } 390 | 391 | if(!last_sector) 392 | { 393 | for(int i=0; i < 0x800; i++) 394 | { 395 | buf[current_fpos + i + 0x18 + 0x930] = sectors[0x800 + i]; // skip 0x18 header info then skip exactly 1 sector. Read the next 0x800 bytes. We now have an array's worth of data from 2 sectors which excludes EDC/Header data at the beginning and end of each. 396 | } 397 | } else { 398 | break; // That was the last sector 399 | } 400 | current_fpos = (current_fpos + 0x930); // Advance one sector. 401 | } 402 | 403 | fseek(bin, 0, SEEK_SET); 404 | fwrite(buf, bin_size, 1, bin); 405 | fclose(bin); 406 | free(buf); 407 | } 408 | 409 | void bin_patch_libcrypt(const char **argv) 410 | { 411 | /* 412 | The AP table could possibly start on the end of a sector and end at the beginning of the next sector. Each RAW sector is 0x930 bytes. The first 0x18 bytes are to be ignored as they are just header data. The next 0x800 bytes contains actual data we want to scan through. 413 | Start at 0. Skip to 0x18. Read the next 0x800 bytes. Skip to a total of 0x930 bytes (one whole raw sector). Skip 0x18 bytes again and then read the next 0x800 bytes. We now have 2 sectors worth of straight up data in a buffer of 0x1000 bytes 414 | Run search functions on the 0x1000 byte sized buffer. 415 | */ 416 | fseek(bin, 0, SEEK_END); 417 | bin_size = ftell(bin); 418 | 419 | if(bin_size > 0x2EE00000) // 750MB max, no PSX software comes even close to such a size 420 | { 421 | printf("Error: The BIN file: %s exceeds the maximum filesize of 750MB in bin patch mode\n", argv[3]); 422 | fclose(bin); 423 | return; 424 | } 425 | 426 | unsigned int magic_word = strtoul(argv[2], NULL, 16); 427 | printf("Magic Word: %08X\n", magic_word); 428 | unsigned char *bytes; 429 | bytes=(unsigned char *)&magic_word; 430 | /* 431 | printf("Bytes 0: %02X\n", bytes[0]); 432 | printf("Bytes 1: %02X\n", bytes[1]); 433 | printf("Bytes 2: %02X\n", bytes[2]); 434 | printf("Bytes 3: %02X\n", bytes[3]); 435 | */ 436 | 437 | fseek(bin, 0, SEEK_SET); 438 | buf = (unsigned char *)malloc(bin_size * sizeof(unsigned char)); // Read entire BIN to memory for performance gain, I mean it's 2022 who doesn't have a free ~700MBs of RAM?! 439 | 440 | if(fread(buf, 1, bin_size, bin) != bin_size) 441 | { 442 | printf("Error loading BIN: file: %s\n", argv[2]); 443 | return; 444 | } 445 | 446 | printf("Successfully loaded BIN file: %s (%d bytes in memory)\n", argv[3], bin_size); 447 | max_size = bin_size; 448 | number_of_sectors = (bin_size / 2352); 449 | printf("Scanning %d sectors, please wait...\n", number_of_sectors); 450 | 451 | libcrypt_2 = true; 452 | 453 | while(1) 454 | { 455 | if(current_fpos > max_size) 456 | break; // even number of sectors, done reading the file. 457 | 458 | if((current_fpos + 0x930) == max_size) // odd number of sectors 459 | last_sector = 1; // This function is reading 2 sectors at a time, so if there is an odd number of sectors we have to change the behavior to only search the last sector. Explicitly break loop when this is set. 460 | 461 | if((current_fpos > (0x930 * 25))) // after 4 sectors (22, 23, 24, 25) stop looking for libcrypt 1 PS-EXE boot filename 462 | directory_record_sectors_maybe = false; 463 | 464 | for(int i=0; i < 0x800; i++) 465 | { 466 | sectors[i] = buf[current_fpos + i + 0x18]; // skip 0x18 header info per sector 467 | } 468 | 469 | if(!last_sector) 470 | { 471 | for(int i=0; i < 0x800; i++) 472 | { 473 | sectors[i + 0x800] = buf[current_fpos + i + 0x18 + 0x930]; // skip 0x18 header info then skip exactly 1 sector. Read the next 0x800 bytes. We now have an array's worth of data from 2 sectors which excludes EDC/Header data at the beggining and end of each. 474 | } 475 | search_size = 0x1000; 476 | } else { 477 | search_size = 0x800; 478 | } 479 | 480 | for(int s = 0; s < search_size; s++) 481 | { 482 | if((directory_record_sectors_maybe) && (!libcrypt_1)) 483 | { 484 | libcrypt_1 = true; 485 | 486 | for(int i=0; i < 13; i++) 487 | { 488 | if( 489 | (medievil_europe_ps_exe[i] != sectors[s + i]) && 490 | (medievil_france_ps_exe[i] != sectors[s + i]) && 491 | (medievil_germany_ps_exe[i] != sectors[s + i]) && 492 | (medievil_italian_ps_exe[i] != sectors[s + i]) && 493 | (medievil_spain_ps_exe[i] != sectors[s + i]) 494 | ) 495 | { 496 | libcrypt_1 = false; 497 | } 498 | } 499 | } 500 | 501 | if(libcrypt_1) 502 | { 503 | matched_libcrypt_1_icepick_based_patch = true; 504 | for(int i=0; i < 47; i++) 505 | { 506 | if(libcrypt_1_medievil_icepick_based_patch[i] != sectors[s + i]) 507 | { 508 | if(i != 1 && i != 4 && i != 5 && i != 6 && i != 7 && i != 8 && i != 9 && i != 13 && i != 17 && i != 25 && i != 29 && i != 33 && i != 41) // These are not matchable so they are 0x00 in the array and not checked here 509 | { 510 | matched_libcrypt_1_icepick_based_patch = false; 511 | } 512 | } 513 | } 514 | 515 | if(matched_libcrypt_1_icepick_based_patch) 516 | { 517 | if(s < 0x800) 518 | { 519 | lba = ((current_fpos / 0x930) + 150); 520 | } else { 521 | lba = ((current_fpos / 0x930) + 151); 522 | } 523 | printf("Got LibCrypt v1 bypass match (By ICEPICK) at LBA: %u\n", lba); 524 | sectors[s + 0] = 0x00; 525 | // skip 1 526 | sectors[s + 2] = 0x00; 527 | sectors[s + 3] = 0x00; 528 | // skip 4 529 | // skip 5 530 | // skip 6 531 | // skip 7 532 | // skip 8 533 | // skip 9 534 | sectors[s + 10] = 0x00; 535 | sectors[s + 11] = 0x00; 536 | sectors[s + 12] = 0x00; 537 | // skip 13 538 | sectors[s + 14] = 0x00; 539 | sectors[s + 15] = 0x00; 540 | sectors[s + 16] = 0x00; 541 | // skip 17 542 | sectors[s + 18] = 0x00; 543 | sectors[s + 19] = 0x00; 544 | sectors[s + 20] = 0x00; 545 | sectors[s + 21] = 0x00; 546 | sectors[s + 22] = 0x00; 547 | sectors[s + 23] = 0x00; 548 | sectors[s + 24] = 0x00; 549 | // skip 25 550 | sectors[s + 26] = 0x00; 551 | sectors[s + 27] = 0x00; 552 | sectors[s + 28] = 0x00; 553 | // skip 29 554 | sectors[s + 30] = 0x00; 555 | sectors[s + 31] = 0x00; 556 | sectors[s + 32] = 0x00; 557 | // skip 33 558 | sectors[s + 34] = 0x00; 559 | sectors[s + 35] = 0x00; 560 | sectors[s + 36] = 0x00; 561 | sectors[s + 37] = 0x00; 562 | sectors[s + 38] = 0x00; 563 | sectors[s + 39] = 0x00; 564 | sectors[s + 40] = 0x00; 565 | // skip 41 566 | sectors[s + 42] = 0x00; 567 | sectors[s + 43] = 0x00; 568 | sectors[s + 44] = bytes[0]; // Magic Word byte 2 569 | sectors[s + 45] = bytes[1]; // Magic Word byte 1 570 | sectors[s + 46] = 0x04; 571 | sectors[s + 47] = 0x24; 572 | } 573 | 574 | } else if(libcrypt_2) { 575 | // LibCrypt v2 (majority of LibCrypt games) 576 | matched_libcrypt_2_anti_pro_action_replay = true; 577 | for(int i=0; i < 8; i++) 578 | { 579 | if(libcrypt_2_anti_pro_action_replay[i] != sectors[s + i]) 580 | matched_libcrypt_2_anti_pro_action_replay = false; 581 | } 582 | 583 | if(matched_libcrypt_2_anti_pro_action_replay) 584 | { 585 | if(s < 0x800) 586 | { 587 | lba = ((current_fpos / 0x930) + 150); 588 | } else { 589 | lba = ((current_fpos / 0x930) + 151); 590 | } 591 | printf("Got LibCrypt v2 Anti-Pro Action Replay bypass match (By B.A.D) at LBA: %u\n", lba); 592 | sectors[s + 0] = 0x00; 593 | sectors[s + 1] = 0x00; 594 | sectors[s + 2] = 0x00; 595 | sectors[s + 3] = 0x00; 596 | sectors[s + 4] = 0x00; 597 | sectors[s + 5] = 0x00; 598 | sectors[s + 6] = 0x00; 599 | sectors[s + 7] = 0x00; 600 | } 601 | 602 | matched_libcrypt_2_anti_mod_chip = true; 603 | for(int i=0; i < 24; i++) 604 | { 605 | if(libcrypt_2_anti_mod_chip[i] != sectors[s + i]) 606 | matched_libcrypt_2_anti_mod_chip = false; 607 | } 608 | 609 | if(matched_libcrypt_2_anti_mod_chip) 610 | { 611 | if(s < 0x800) 612 | { 613 | lba = ((current_fpos / 0x930) + 150); 614 | } else { 615 | lba = ((current_fpos / 0x930) + 151); 616 | } 617 | printf("Got LibCrypt v2 Anti-Mod-Chip bypass match (By B.A.D) at LBA: %u\n", lba); 618 | // Part 1 619 | sectors[s + 0] = 0x00; 620 | sectors[s + 1] = 0x00; 621 | sectors[s + 2] = 0x00; 622 | sectors[s + 3] = 0x00; 623 | // Part 2 624 | sectors[s + 4] = 0x02; 625 | sectors[s + 5] = 0x00; 626 | sectors[s + 6] = 0xE7; 627 | sectors[s + 7] = 0x30; 628 | // Part 3 629 | sectors[s + 8] = 0x00; 630 | sectors[s + 9] = 0x00; 631 | sectors[s + 10] = 0x00; 632 | sectors[s + 11] = 0x00; 633 | // Part 4 634 | sectors[s + 12] = 0xAD; 635 | sectors[s + 13] = 0xFF; 636 | sectors[s + 14] = 0x84; 637 | sectors[s + 15] = 0x20; 638 | // Part 5 639 | sectors[s + 16] = 0x00; 640 | sectors[s + 17] = 0x00; 641 | sectors[s + 18] = 0x00; 642 | sectors[s + 19] = 0x00; 643 | // Part 6 644 | sectors[s + 20] = 0x00; 645 | sectors[s + 21] = 0x00; 646 | sectors[s + 22] = 0x00; 647 | sectors[s + 23] = 0x00; 648 | } 649 | 650 | matched_libcrypt_2_magic_word = true; 651 | for(int i=0; i < 4; i++) 652 | { 653 | if(libcrypt_2_magic_word[i] != sectors[s + i]) 654 | matched_libcrypt_2_magic_word = false; 655 | } 656 | 657 | if(matched_libcrypt_2_magic_word) 658 | { 659 | if(s < 0x800) 660 | { 661 | lba = ((current_fpos / 0x930) + 150); 662 | } else { 663 | lba = ((current_fpos / 0x930) + 151); 664 | } 665 | printf("Got LibCrypt v2 Magic Word bypass match (By B.A.D) at LBA: %u\n", lba); 666 | sectors[s + 0] = bytes[0]; 667 | sectors[s + 1] = bytes[1]; 668 | sectors[s + 2] = 0xC6; 669 | sectors[s + 3] = 0x34; 670 | // 6C 3A C6 34 671 | libcrypt_2 = false; 672 | } 673 | } 674 | } 675 | 676 | for(int i=0; i < 0x800; i++) 677 | { 678 | buf[current_fpos + i + 0x18] = sectors[i]; // skip 0x18 header info per sector 679 | } 680 | 681 | if(!last_sector) 682 | { 683 | for(int i=0; i < 0x800; i++) 684 | { 685 | buf[current_fpos + i + 0x18 + 0x930] = sectors[0x800 + i]; // skip 0x18 header info then skip exactly 1 sector. Read the next 0x800 bytes. We now have an array's worth of data from 2 sectors which excludes EDC/Header data at the beginning and end of each. 686 | } 687 | } else { 688 | break; // That was the last sector 689 | } 690 | current_fpos = (current_fpos + 0x930); // Advance one sector. 691 | } 692 | 693 | fseek(bin, 0, SEEK_SET); 694 | fwrite(buf, bin_size, 1, bin); 695 | fclose(bin); 696 | free(buf); 697 | } 698 | 699 | void bin_patch(const char **argv) 700 | { 701 | bool is_beatmania_append_gotta_mix = false; 702 | bool is_beatmania_append_3rd_mix = false; 703 | 704 | /* 705 | The AP table could possibly start on the end of a sector and end at the beginning of the next sector. Each RAW sector is 0x930 bytes. The first 0x18 bytes are to be ignored as they are just header data. The next 0x800 bytes contains actual data we want to scan through. 706 | Start at 0. Skip to 0x18. Read the next 0x800 bytes. Skip to a total of 0x930 bytes (one whole raw sector). Skip 0x18 bytes again and then read the next 0x800 bytes. We now have 2 sectors worth of straight up data in a buffer of 0x1000 bytes 707 | Run search functions on the 0x1000 byte sized buffer. 708 | */ 709 | fseek(bin, 0, SEEK_END); 710 | bin_size = ftell(bin); 711 | 712 | if(bin_size > 0x2EE00000) // 750MB max, no PSX software comes even close to such a size 713 | { 714 | printf("Error: The CD image BIN file %s exceeds the maximum filesize of 750MB\n", argv[2]); 715 | fclose(bin); 716 | return; 717 | } 718 | 719 | fseek(bin, 0, SEEK_SET); 720 | buf = (unsigned char *)malloc(bin_size * sizeof(unsigned char)); // Read entire BIN to memory for performance gain, I mean it's 2022 who doesn't have a free ~700MBs of RAM?! 721 | 722 | if(fread(buf, 1, bin_size, bin) != bin_size) 723 | { 724 | printf("Error loading CD image BIN file: %s\n", argv[2]); 725 | return; 726 | } 727 | 728 | printf("Successfully loaded CD image BIN file: %s (%d bytes in memory)\n", argv[2], bin_size); 729 | max_size = bin_size; 730 | number_of_sectors = (bin_size / 2352); 731 | printf("Scanning %d sectors, please wait...\n", number_of_sectors); 732 | 733 | while(1) 734 | { 735 | if(current_fpos > max_size) 736 | break; // even number of sectors, done reading the file. 737 | 738 | if((current_fpos + 0x930) == max_size) // odd number of sectors 739 | last_sector = 1; // This function is reading 2 sectors at a time, so if there is an odd number of sectors we have to change the behavior to only search the last sector. Explicitly break loop when this is set. 740 | 741 | for(int i=0; i < 0x800; i++) 742 | { 743 | sectors[i] = buf[current_fpos + i + 0x18]; // skip 0x18 header info per sector 744 | } 745 | 746 | if(!last_sector) 747 | { 748 | for(int i=0; i < 0x800; i++) 749 | { 750 | sectors[i + 0x800] = buf[current_fpos + i + 0x18 + 0x930]; // skip 0x18 header info then skip exactly 1 sector. Read the next 0x800 bytes. We now have an array's worth of data from 2 sectors which excludes EDC/Header data at the beginning and end of each. 751 | } 752 | search_size = 0x1000; 753 | } else { 754 | search_size = 0x800; 755 | } 756 | 757 | for(int s = 0; s < search_size; s++) 758 | { 759 | // Sector 23 is where no swap matches occur (which is within directory sectors maybe range) 760 | if((directory_record_sectors_maybe) && (!is_beatmania_append_gotta_mix)) 761 | { 762 | is_beatmania_append_gotta_mix = true; 763 | 764 | for(int i=0; i < 66; i++) 765 | { 766 | if(beatmania_append_gotta_mix_system_cnf[i] != sectors[s + i]) 767 | { 768 | is_beatmania_append_gotta_mix = false; 769 | } 770 | } 771 | 772 | if(is_beatmania_append_gotta_mix) 773 | { 774 | 775 | if(s < 0x800) 776 | { 777 | lba = ((current_fpos / 0x930) + 150); 778 | } else { 779 | lba = ((current_fpos / 0x930) + 151); 780 | } 781 | 782 | printf("Got Append No Swap Bypass match (By mdmdj) at LBA: %u\n", lba); 783 | sectors[s + 14] = append_exe_name[0]; // / 784 | sectors[s + 15] = append_exe_name[1]; // A 785 | sectors[s + 16] = append_exe_name[2]; // P 786 | sectors[s + 17] = append_exe_name[3]; // P 787 | sectors[s + 18] = append_exe_name[4]; // E 788 | sectors[s + 19] = append_exe_name[5]; // N 789 | sectors[s + 20] = append_exe_name[6]; // D 790 | sectors[s + 21] = append_exe_name[7]; // . 791 | sectors[s + 22] = append_exe_name[8]; // E 792 | sectors[s + 23] = append_exe_name[9]; // X 793 | sectors[s + 24] = append_exe_name[10]; // E 794 | } 795 | } 796 | 797 | if((directory_record_sectors_maybe) && (!is_beatmania_append_3rd_mix)) 798 | { 799 | is_beatmania_append_3rd_mix = true; 800 | 801 | for(int i=0; i < 66; i++) 802 | { 803 | if(beatmania_append_3rd_mix_system_cnf[i] != sectors[s + i]) 804 | { 805 | is_beatmania_append_3rd_mix = false; 806 | } 807 | } 808 | 809 | if(is_beatmania_append_3rd_mix) 810 | { 811 | 812 | if(s < 0x800) 813 | { 814 | lba = ((current_fpos / 0x930) + 150); 815 | } else { 816 | lba = ((current_fpos / 0x930) + 151); 817 | } 818 | 819 | printf("Got Append.exe bypass match (By mdmdj) at LBA: %u\n", lba); 820 | sectors[s + 14] = append_exe_name[0]; // / 821 | sectors[s + 15] = append_exe_name[1]; // A 822 | sectors[s + 16] = append_exe_name[2]; // P 823 | sectors[s + 17] = append_exe_name[3]; // P 824 | sectors[s + 18] = append_exe_name[4]; // E 825 | sectors[s + 19] = append_exe_name[5]; // N 826 | sectors[s + 20] = append_exe_name[6]; // D 827 | sectors[s + 21] = append_exe_name[7]; // . 828 | sectors[s + 22] = append_exe_name[8]; // E 829 | sectors[s + 23] = append_exe_name[9]; // X 830 | sectors[s + 24] = append_exe_name[10]; // E 831 | } 832 | } 833 | 834 | matched_anti_piracy_v1 = true; 835 | for(int i=0; i < 40; i++) 836 | { 837 | if(anti_piracy_v1[i] != sectors[s + i]) 838 | { 839 | if(i != 3 && i != 7 && i != 11 && i != 15 && i != 19 && i != 23 && i != 27 && i != 31 && i != 35 && i != 39) // These bytes could change, they can be 03 or 05 depending on the AP code in the game but the table itself remains consistent besides the value of every 4th byte and the commands are still obvious via this pattern 840 | matched_anti_piracy_v1 = false; 841 | } 842 | } 843 | 844 | if(matched_anti_piracy_v1) 845 | { 846 | if(s < 0x800) 847 | { 848 | lba = ((current_fpos / 0x930) + 150); 849 | } else { 850 | lba = ((current_fpos / 0x930) + 151); 851 | } 852 | printf("Got APv1 Zero bypass match (By Alex Free) at LBA: %u\n", lba); 853 | sectors[s + 20] = sync; // Replace SubFunq X's bytes with '00' bytes 854 | sectors[s + 21] = sync; 855 | sectors[s + 22] = sync; 856 | sectors[s + 23] = sync; 857 | 858 | sectors[s + 32] = sync; /// Replace SubFunq Y's bytes with '00' bytes 859 | sectors[s + 33] = sync; 860 | sectors[s + 34] = sync; 861 | sectors[s + 35] = sync; 862 | } 863 | 864 | matched_anti_piracy_v2_vc0_bypass = true; 865 | for(int i=0; i < 52; i++) 866 | { 867 | 868 | if(anti_piracy_v2_vc0_bypass[i] != sectors[s + i]) 869 | { 870 | if(i != 3 && i != 7 && i != 11 && i != 15 && i != 19 && i != 23 && i != 27 && i != 31 && i != 35 && i != 39 && i != 43 && i != 47 && i != 51) // These bytes could change, they can be 03 or 05 depending on the AP code in the game but the table itself remains consistent besides the value of every 4th byte and the commands are still obvious via this pattern 871 | { 872 | matched_anti_piracy_v2_vc0_bypass = false; 873 | } 874 | } 875 | } 876 | 877 | if(matched_anti_piracy_v2_vc0_bypass) 878 | { 879 | if(s < 0x800) 880 | { 881 | lba = ((current_fpos / 0x930) + 150); 882 | } else { 883 | lba = ((current_fpos / 0x930) + 151); 884 | } 885 | printf("Got APv2 Fake VC0 bypass match (By Alex Free) at LBA: %u\n", lba); 886 | sectors[s + 48] = sync; // Replace ReadTOC's first byte with the first byte of the sync command. This seems to trigger the VC0 CDROM Controller BIOS Firmware behavior on all consoles. The VC0 CDROM Controller BIOS firmware does not have the ReadTOC command, it is found in the wild in early SCPH-3000 Japanese consoles and in all SCPH-1000 consoles. 887 | } 888 | 889 | matched_anti_piracy_v2_pal_bypass = true; 890 | for(int i=0; i < 16; i++) 891 | { 892 | if(anti_piracy_v2_pal_bypass[i] != sectors[s + i]) 893 | matched_anti_piracy_v2_pal_bypass = false; 894 | } 895 | 896 | if(matched_anti_piracy_v2_pal_bypass) 897 | { 898 | if(s < 0x800) 899 | { 900 | lba = ((current_fpos / 0x930) + 150); 901 | } else { 902 | lba = ((current_fpos / 0x930) + 151); 903 | } 904 | printf("Got APv2 Fake PAL BIOS bypass match (By Alex Free & MottZilla) at LBA: %u\n", lba); 905 | sectors[s + 14] = 0x00; 906 | sectors[s + 15] = 0x18; 907 | // 0x00, 0x18 to make the game think your using a PAL BIOS/console 908 | } 909 | } 910 | 911 | for(int i=0; i < 0x800; i++) 912 | { 913 | buf[current_fpos + i + 0x18] = sectors[i]; // skip 0x18 header info per sector 914 | } 915 | 916 | if(!last_sector) 917 | { 918 | for(int i=0; i < 0x800; i++) 919 | { 920 | buf[current_fpos + i + 0x18 + 0x930] = sectors[0x800 + i]; // skip 0x18 header info then skip exactly 1 sector. Read the next 0x800 bytes. We now have an array's worth of data from 2 sectors which excludes EDC/Header data at the beginning and end of each. 921 | } 922 | } else { 923 | break; // That was the last sector 924 | } 925 | current_fpos = (current_fpos + 0x930); // Advance one sector. 926 | } 927 | 928 | fseek(bin, 0, SEEK_SET); 929 | fwrite(buf, bin_size, 1, bin); 930 | fclose(bin); 931 | free(buf); 932 | } 933 | 934 | void gameshark_gen(const char **argv) 935 | { 936 | unsigned int libcrypt_2_anti_mod_chip_offset = 0; 937 | unsigned int libcrypt_2_anti_pro_action_replay_offset = 0; 938 | 939 | fseek(mem_dump_1, 0, SEEK_END); 940 | mem_dump_1_size = ftell(mem_dump_1); 941 | 942 | if(mem_dump_1_size != valid_mem_dump_size) 943 | { 944 | printf("Error: the original game's RAM dump file: %s is not the expected size\n", argv[2]); 945 | fclose(mem_dump_1); 946 | free(buf); 947 | if(mem_dump_1_size == 8388608) 948 | printf("Do you have the 'Enable 8MB RAM' option enabled? Uncheck that option if so and make a new RAM dump\n"); 949 | return; 950 | } 951 | 952 | fseek(mem_dump_1, 0, SEEK_SET); 953 | buf = (unsigned char *)malloc(mem_dump_1_size * sizeof(unsigned char)); 954 | 955 | if(fread(buf, 1, mem_dump_1_size, mem_dump_1) != mem_dump_1_size) 956 | { 957 | printf("Error loading RAM dump file: %s\n", argv[2]); 958 | return; 959 | } 960 | 961 | printf("Loaded RAM dump file: %s (%d bytes in memory)\n", argv[2], mem_dump_1_size); 962 | 963 | while(1) 964 | { 965 | if(current_fpos > valid_mem_dump_size) 966 | break; 967 | 968 | if(!libcrypt) 969 | { 970 | matched_anti_piracy_v1 = true; 971 | for(int i=0; i < 40; i++) 972 | { 973 | if(anti_piracy_v1[i] != buf[current_fpos + i]) 974 | { 975 | if(i != 3 && i != 7 && i != 11 && i != 15 && i != 19 && i != 23 && i != 27 && i != 31 && i != 35 && i != 39) // These bytes could change, they can be 03 or 05 depending on the AP code in the game but the table itself remains consistent besides the value of every 4th byte and the commands are still obvious via this pattern 976 | matched_anti_piracy_v1 = false; 977 | } 978 | } 979 | 980 | if(matched_anti_piracy_v1) 981 | { 982 | printf("\nGot APv1 Zero bypass (By Alex Free) starting at offset: 0x%08X\n", current_fpos); 983 | gameshark_write_byte_address = (current_fpos + 20); // SubFunq X (19'04) 984 | 985 | // First 2 bytes of 19'04 986 | bytes=(unsigned char *)&gameshark_write_byte_address; // pos of 20 from match base 987 | bytes[3] = 0xD0; 988 | printf("%08X 0119\n", gameshark_write_byte_address); // Look for SubFunq X (first 2 bytes) 989 | bytes=(unsigned char *)&gameshark_write_byte_address; 990 | bytes[3] = 0x80; 991 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 992 | printf("%08X 0000\n\n", gameshark_write_byte_address); // Replace SubFunq X's first 2 bytes with '00's 993 | 994 | // Last 2 bytes of 19'04 995 | gameshark_write_byte_address = (gameshark_write_byte_address + 2); // pos of 22 from match base 996 | bytes=(unsigned char *)&gameshark_write_byte_address; 997 | last_byte = buf[current_fpos + 23]; 998 | bytes[3] = 0xD0; 999 | printf("%08X %02X04\n", gameshark_write_byte_address, last_byte); // Look for SubFunq X (last 2 bytes). The last byte can change, usually it is 0x03 or 0x05. So we get that byte from the mem dump file. 1000 | bytes=(unsigned char *)&gameshark_write_byte_address; 1001 | bytes[3] = 0x80; 1002 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1003 | printf("%08X 0000\n\n", gameshark_write_byte_address); // Replace SubFunq X's last two bytes with '00's 1004 | 1005 | // First 2 bytes of 19'05 1006 | gameshark_write_byte_address = (gameshark_write_byte_address + 10); // pos of 32 from match base 1007 | bytes=(unsigned char *)&gameshark_write_byte_address; 1008 | bytes[3] = 0xD0; 1009 | printf("%08X 0119\n", gameshark_write_byte_address); // Look for SubFunq Y (first 2 bytes) 1010 | bytes=(unsigned char *)&gameshark_write_byte_address; 1011 | bytes[3] = 0x80; 1012 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1013 | printf("%08X 0000\n\n", gameshark_write_byte_address); // Replace SubFunq Y's first 2 bytes with '00's 1014 | 1015 | // Second 2 bytes of 19'05 1016 | gameshark_write_byte_address = (gameshark_write_byte_address + 2); // pos of 34 from match base 1017 | last_byte = buf[current_fpos + 35]; 1018 | bytes=(unsigned char *)&gameshark_write_byte_address; 1019 | bytes[3] = 0xD0; 1020 | printf("%08X %02X02\n", gameshark_write_byte_address, last_byte); // Look for SubFunq Y (last 2 bytes). The last byte can change, usually it is 0x03 or 0x05. So we get that byte from the mem dump file. 1021 | bytes=(unsigned char *)&gameshark_write_byte_address; 1022 | bytes[3] = 0x80; 1023 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1024 | printf("%08X 0000\n\n", gameshark_write_byte_address); // Replace SubFunq Y's last 2 bytes with '00's 1025 | 1026 | current_fpos = (current_fpos + 40); // Skip the next 40 bytes as we already know this is the table 1027 | } 1028 | 1029 | matched_anti_piracy_v2_vc0_bypass = true; 1030 | for(int i=0; i < 52; i++) 1031 | { 1032 | if(anti_piracy_v2_vc0_bypass[i] != buf[current_fpos + i]) 1033 | { 1034 | if(i != 3 && i != 7 && i != 11 && i != 15 && i != 19 && i != 23 && i != 27 && i != 31 && i != 35 && i != 39 && i != 43 && i != 47 && i != 51) // These bytes could change, they can be 03 or 05 depending on the AP code in the game but the table itself remains consistent besides the value of every 4th byte and the commands are still obvious via this pattern 1035 | matched_anti_piracy_v2_vc0_bypass = false; 1036 | } 1037 | } 1038 | 1039 | if(matched_anti_piracy_v2_vc0_bypass) 1040 | { 1041 | printf("Got APv2 Fake VC0 Bypass starting at offset: 0x%08X\n", current_fpos); 1042 | gameshark_write_byte_address = (current_fpos + 48); 1043 | 1044 | bytes=(unsigned char *)&gameshark_write_byte_address; 1045 | bytes[3] = 0xD0; 1046 | printf("%08X 001E\n", gameshark_write_byte_address); // Look for ReadTOC command 1047 | bytes=(unsigned char *)&gameshark_write_byte_address; 1048 | bytes[3] = 0x80; 1049 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1050 | printf("%08X 0000\n", gameshark_write_byte_address); // Write the sync command where ReadTOC was. This seems to trigger the VC0 CDROM Controller BIOS Firmware behavior on all consoles. The VC0 CDROM Controller BIOS firmware does not have the ReadTOC command, it is found in the wild in early SCPH-3000 Japanese consoles and in all SCPH-1000 consoles. 1051 | current_fpos = (current_fpos + 52); // Skip the next 52 bytes as we already know this is the table 1052 | } 1053 | 1054 | matched_anti_piracy_v2_pal_bypass = true; 1055 | for(int i=0; i < 16; i++) 1056 | { 1057 | if(anti_piracy_v2_pal_bypass[i] != buf[current_fpos + i]) 1058 | matched_anti_piracy_v2_pal_bypass = false; 1059 | } 1060 | 1061 | if(matched_anti_piracy_v2_pal_bypass) 1062 | { 1063 | printf("Got APv2 Fake PAL BIOS bypass (By Alex Free & MottZilla) starting at offset: 0x%08X\n", current_fpos); 1064 | gameshark_write_byte_address = (current_fpos + 14); 1065 | // 0x0A, 0x00, 0x62, 0x10 // change to 0x0A, 0x00, 0x00, 0x18 to make the game think your using a PAL BIOS/console 1066 | bytes=(unsigned char *)&gameshark_write_byte_address; 1067 | bytes[3] = 0xD0; 1068 | printf("%08X 1062\n", gameshark_write_byte_address); 1069 | bytes=(unsigned char *)&gameshark_write_byte_address; 1070 | bytes[3] = 0x80; 1071 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1072 | printf("%08X 1800\n", gameshark_write_byte_address); 1073 | current_fpos = (current_fpos + 16); // Skip the next 16 bytes as we already know this is the match just confirmed 1074 | } 1075 | 1076 | 1077 | } else if(libcrypt) { 1078 | matched_libcrypt_1_icepick_based_patch = true; 1079 | for(int i=0; i < 47; i++) 1080 | { 1081 | if(libcrypt_1_medievil_icepick_based_patch[i] != buf[current_fpos + i]) 1082 | { 1083 | if(i != 1 && i != 4 && i != 5 && i != 6 && i != 7 && i != 8 && i != 9 && i != 13 && i != 17 && i != 25 && i != 29 && i != 33 && i != 41) // These are not matchable so they are 0x00 in the array and not checked here 1084 | { 1085 | matched_libcrypt_1_icepick_based_patch = false; 1086 | } 1087 | } 1088 | } 1089 | 1090 | if(matched_libcrypt_1_icepick_based_patch) 1091 | { 1092 | printf("Got LibCrypt v1 bypass (By ICEPICK) match starting at offset: 0x%08X\n\n", current_fpos); 1093 | 1094 | // offset 0 = 0x0A?? -> 0x00?? 1095 | gameshark_write_byte_address = current_fpos; 1096 | bytes=(unsigned char *)&gameshark_write_byte_address; 1097 | bytes[3] = 0xD0; 1098 | printf("%08X %02X0A\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1099 | bytes=(unsigned char *)&gameshark_write_byte_address; 1100 | bytes[3] = 0x80; 1101 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1102 | printf("%08X %02X00\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1103 | 1104 | // offset 2 = 0x8014 -> 0x0000 1105 | gameshark_write_byte_address = (current_fpos + 2); 1106 | bytes=(unsigned char *)&gameshark_write_byte_address; 1107 | bytes[3] = 0xD0; 1108 | printf("%08X 1480\n", gameshark_write_byte_address); 1109 | bytes=(unsigned char *)&gameshark_write_byte_address; 1110 | bytes[3] = 0x80; 1111 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1112 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1113 | 1114 | // offset 10 = 0xA390 -> 0x0000 1115 | gameshark_write_byte_address = (current_fpos + 10); 1116 | bytes=(unsigned char *)&gameshark_write_byte_address; 1117 | bytes[3] = 0xD0; 1118 | printf("%08X 90A3\n", gameshark_write_byte_address); 1119 | bytes=(unsigned char *)&gameshark_write_byte_address; 1120 | bytes[3] = 0x80; 1121 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1122 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1123 | 1124 | // offset 12 = 0x02?? -> 0x0000 1125 | gameshark_write_byte_address = (current_fpos + 12); 1126 | bytes=(unsigned char *)&gameshark_write_byte_address; 1127 | bytes[3] = 0xD0; 1128 | printf("%08X %02X02\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1129 | bytes=(unsigned char *)&gameshark_write_byte_address; 1130 | bytes[3] = 0x80; 1131 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1132 | printf("%08X %02X00\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1133 | 1134 | // offset 14 = 0x0224 -> 0x0000 1135 | gameshark_write_byte_address = (current_fpos + 14); 1136 | bytes=(unsigned char *)&gameshark_write_byte_address; 1137 | bytes[3] = 0xD0; 1138 | printf("%08X 2402\n", gameshark_write_byte_address); 1139 | bytes=(unsigned char *)&gameshark_write_byte_address; 1140 | bytes[3] = 0x80; 1141 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1142 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1143 | 1144 | // offset 16 = 0x06?? -> 0x00?? 1145 | gameshark_write_byte_address = (current_fpos + 16); 1146 | bytes=(unsigned char *)&gameshark_write_byte_address; 1147 | bytes[3] = 0xD0; 1148 | printf("%08X %02X06\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1149 | bytes=(unsigned char *)&gameshark_write_byte_address; 1150 | bytes[3] = 0x80; 1151 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1152 | printf("%08X %02X00\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1153 | 1154 | // offset 18 = 0x6214 -> 0x0000 1155 | gameshark_write_byte_address = (current_fpos + 18); 1156 | bytes=(unsigned char *)&gameshark_write_byte_address; 1157 | bytes[3] = 0xD0; 1158 | printf("%08X 1462\n", gameshark_write_byte_address); 1159 | bytes=(unsigned char *)&gameshark_write_byte_address; 1160 | bytes[3] = 0x80; 1161 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1162 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1163 | 1164 | // offset 20 = 0x0E80 -> 0x0000 1165 | gameshark_write_byte_address = (current_fpos + 20); 1166 | bytes=(unsigned char *)&gameshark_write_byte_address; 1167 | bytes[3] = 0xD0; 1168 | printf("%08X 800E\n", gameshark_write_byte_address); 1169 | bytes=(unsigned char *)&gameshark_write_byte_address; 1170 | bytes[3] = 0x80; 1171 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1172 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1173 | 1174 | // offset 22 = 0x033C -> 0x0000 1175 | gameshark_write_byte_address = (current_fpos + 22); 1176 | bytes=(unsigned char *)&gameshark_write_byte_address; 1177 | bytes[3] = 0xD0; 1178 | printf("%08X 3C03\n", gameshark_write_byte_address); 1179 | bytes=(unsigned char *)&gameshark_write_byte_address; 1180 | bytes[3] = 0x80; 1181 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1182 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1183 | 1184 | // offset 24 = 0x04?? -> 0x00?? 1185 | gameshark_write_byte_address = (current_fpos + 24); 1186 | bytes=(unsigned char *)&gameshark_write_byte_address; 1187 | bytes[3] = 0xD0; 1188 | printf("%08X %02X04\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1189 | bytes=(unsigned char *)&gameshark_write_byte_address; 1190 | bytes[3] = 0x80; 1191 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1192 | printf("%08X 00%02X\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1193 | 1194 | // offset 26 = 0xA390 -> 0x0000 1195 | gameshark_write_byte_address = (current_fpos + 26); 1196 | bytes=(unsigned char *)&gameshark_write_byte_address; 1197 | bytes[3] = 0xD0; 1198 | printf("%08X 90A3\n", gameshark_write_byte_address); 1199 | bytes=(unsigned char *)&gameshark_write_byte_address; 1200 | bytes[3] = 0x80; 1201 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1202 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1203 | 1204 | // offset 28 = 0x53?? -> 0x00?? 1205 | gameshark_write_byte_address = (current_fpos + 28); 1206 | bytes=(unsigned char *)&gameshark_write_byte_address; 1207 | bytes[3] = 0xD0; 1208 | printf("%08X %02X53\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1209 | bytes=(unsigned char *)&gameshark_write_byte_address; 1210 | bytes[3] = 0x80; 1211 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1212 | printf("%08X %02X00\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1213 | 1214 | // offset 30 = 0x0224 -> 0x0000 1215 | gameshark_write_byte_address = (current_fpos + 30); 1216 | bytes=(unsigned char *)&gameshark_write_byte_address; 1217 | bytes[3] = 0xD0; 1218 | printf("%08X 2402\n", gameshark_write_byte_address); 1219 | bytes=(unsigned char *)&gameshark_write_byte_address; 1220 | bytes[3] = 0x80; 1221 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1222 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1223 | 1224 | // offset 32 = 0x02?? -> 0x00?? 1225 | gameshark_write_byte_address = (current_fpos + 32); 1226 | bytes=(unsigned char *)&gameshark_write_byte_address; 1227 | bytes[3] = 0xD0; 1228 | printf("%08X %02X02\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1229 | bytes=(unsigned char *)&gameshark_write_byte_address; 1230 | bytes[3] = 0x80; 1231 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1232 | printf("%08X %02X00\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1233 | 1234 | // offset 34 = 0x6214 -> 0x6214 1235 | gameshark_write_byte_address = (current_fpos + 34); 1236 | bytes=(unsigned char *)&gameshark_write_byte_address; 1237 | bytes[3] = 0xD0; 1238 | printf("%08X 1462\n", gameshark_write_byte_address); 1239 | bytes=(unsigned char *)&gameshark_write_byte_address; 1240 | bytes[3] = 0x80; 1241 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1242 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1243 | 1244 | // offset 36 = 0x6214 -> 0x6214 1245 | gameshark_write_byte_address = (current_fpos + 36); 1246 | bytes=(unsigned char *)&gameshark_write_byte_address; 1247 | bytes[3] = 0xD0; 1248 | printf("%08X 800E\n", gameshark_write_byte_address); 1249 | bytes=(unsigned char *)&gameshark_write_byte_address; 1250 | bytes[3] = 0x80; 1251 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1252 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1253 | 1254 | // offset 38 = 0x033C -> 0x6214 1255 | gameshark_write_byte_address = (current_fpos + 38); 1256 | bytes=(unsigned char *)&gameshark_write_byte_address; // pos of 22 from match base 1257 | bytes[3] = 0xD0; 1258 | printf("%08X 3C03\n", gameshark_write_byte_address); 1259 | bytes=(unsigned char *)&gameshark_write_byte_address; 1260 | bytes[3] = 0x80; 1261 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1262 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1263 | 1264 | // offset 40 = 0x07?? -> 0x00?? 1265 | gameshark_write_byte_address = (current_fpos + 40); 1266 | bytes=(unsigned char *)&gameshark_write_byte_address; 1267 | bytes[3] = 0xD0; 1268 | printf("%08X %02X07\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1269 | bytes=(unsigned char *)&gameshark_write_byte_address; 1270 | bytes[3] = 0x80; 1271 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1272 | printf("%08X %02X00\n\n", gameshark_write_byte_address, buf[current_fpos + 1]); 1273 | 1274 | // offset 42 = 0xA490 -> 0x0000 1275 | gameshark_write_byte_address = (current_fpos + 42); 1276 | bytes=(unsigned char *)&gameshark_write_byte_address; 1277 | bytes[3] = 0xD0; 1278 | printf("%08X 90A4\n", gameshark_write_byte_address); 1279 | bytes=(unsigned char *)&gameshark_write_byte_address; 1280 | bytes[3] = 0x80; 1281 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1282 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1283 | 1284 | // offset 44 = 0x0000 -> 1285 | gameshark_write_byte_address = (current_fpos + 44); 1286 | bytes=(unsigned char *)&gameshark_write_byte_address; 1287 | bytes[3] = 0xD0; 1288 | printf("%08X 0000\n", gameshark_write_byte_address); 1289 | bytes=(unsigned char *)&gameshark_write_byte_address; 1290 | bytes[3] = 0x80; 1291 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1292 | printf("%08X %04X\n\n", gameshark_write_byte_address, magic_word); 1293 | 1294 | // offset 46 = 0x0000 -> 0424 1295 | gameshark_write_byte_address = (current_fpos + 46); 1296 | bytes=(unsigned char *)&gameshark_write_byte_address; 1297 | bytes[3] = 0xD0; 1298 | printf("%08X 0000\n", gameshark_write_byte_address); 1299 | bytes=(unsigned char *)&gameshark_write_byte_address; 1300 | bytes[3] = 0x80; 1301 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1302 | printf("%08X 2404\n\n", gameshark_write_byte_address); 1303 | break; // nothing more to do 1304 | } 1305 | 1306 | matched_libcrypt_2_anti_pro_action_replay = true; 1307 | for(int i=0; i < 8; i++) 1308 | { 1309 | if(libcrypt_2_anti_pro_action_replay[i] != buf[current_fpos + i]) 1310 | matched_libcrypt_2_anti_pro_action_replay = false; 1311 | } 1312 | 1313 | if(matched_libcrypt_2_anti_pro_action_replay) 1314 | { 1315 | libcrypt_2_anti_pro_action_replay_offset = current_fpos; 1316 | // offset 0 = 0x80E1 -> 0x0000 1317 | gameshark_write_byte_address = (current_fpos + 0); 1318 | bytes=(unsigned char *)&gameshark_write_byte_address; 1319 | bytes[3] = 0xD0; 1320 | printf("%08X E180\n", gameshark_write_byte_address); 1321 | bytes=(unsigned char *)&gameshark_write_byte_address; 1322 | bytes[3] = 0x80; 1323 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1324 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1325 | 1326 | // offset 2 = 0x023C -> 0x0000 1327 | gameshark_write_byte_address = (current_fpos + 2); 1328 | bytes=(unsigned char *)&gameshark_write_byte_address; 1329 | bytes[3] = 0xD0; 1330 | printf("%08X 3C02\n", gameshark_write_byte_address); 1331 | bytes=(unsigned char *)&gameshark_write_byte_address; 1332 | bytes[3] = 0x80; 1333 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1334 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1335 | 1336 | // offset 4 = 0x0038 -> 0x0000 1337 | gameshark_write_byte_address = (current_fpos + 4); 1338 | bytes=(unsigned char *)&gameshark_write_byte_address; 1339 | bytes[3] = 0xD0; 1340 | printf("%08X 3800\n", gameshark_write_byte_address); 1341 | bytes=(unsigned char *)&gameshark_write_byte_address; 1342 | bytes[3] = 0x80; 1343 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1344 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1345 | 1346 | // offset 6 = 0x8240 -> 0x0000 1347 | gameshark_write_byte_address = (current_fpos + 6); 1348 | bytes=(unsigned char *)&gameshark_write_byte_address; 1349 | bytes[3] = 0xD0; 1350 | printf("%08X 4082\n", gameshark_write_byte_address); 1351 | bytes=(unsigned char *)&gameshark_write_byte_address; 1352 | bytes[3] = 0x80; 1353 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1354 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1355 | } 1356 | 1357 | matched_libcrypt_2_anti_mod_chip = true; 1358 | for(int i=0; i < 24; i++) 1359 | { 1360 | if(libcrypt_2_anti_mod_chip[i] != buf[current_fpos + i]) 1361 | matched_libcrypt_2_anti_mod_chip = false; 1362 | } 1363 | 1364 | if(matched_libcrypt_2_anti_mod_chip) 1365 | { 1366 | libcrypt_2_anti_mod_chip_offset = current_fpos; 1367 | // offset 0 = 0x0800 -> 0x0000 1368 | gameshark_write_byte_address = (current_fpos + 0); 1369 | bytes=(unsigned char *)&gameshark_write_byte_address; 1370 | bytes[3] = 0xD0; 1371 | printf("%08X 0008\n", gameshark_write_byte_address); 1372 | bytes=(unsigned char *)&gameshark_write_byte_address; 1373 | bytes[3] = 0x80; 1374 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1375 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1376 | 1377 | // offset 2 = 0x2014 -> 0x0000 1378 | gameshark_write_byte_address = (current_fpos + 2); 1379 | bytes=(unsigned char *)&gameshark_write_byte_address; 1380 | bytes[3] = 0xD0; 1381 | printf("%08X 1420\n", gameshark_write_byte_address); 1382 | bytes=(unsigned char *)&gameshark_write_byte_address; 1383 | bytes[3] = 0x80; 1384 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1385 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1386 | 1387 | // offset 8 = 0x0600 -> 0x0000 1388 | gameshark_write_byte_address = (current_fpos + 8); 1389 | bytes=(unsigned char *)&gameshark_write_byte_address; 1390 | bytes[3] = 0xD0; 1391 | printf("%08X 0006\n", gameshark_write_byte_address); 1392 | bytes=(unsigned char *)&gameshark_write_byte_address; 1393 | bytes[3] = 0x80; 1394 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1395 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1396 | 1397 | // offset 10 = 0xE010 -> 0x0000 1398 | gameshark_write_byte_address = (current_fpos + 10); 1399 | bytes=(unsigned char *)&gameshark_write_byte_address; 1400 | bytes[3] = 0xD0; 1401 | printf("%08X 10E0\n", gameshark_write_byte_address); 1402 | bytes=(unsigned char *)&gameshark_write_byte_address; 1403 | bytes[3] = 0x80; 1404 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1405 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1406 | 1407 | // offset 16 = 0x0400 -> 0x0000 1408 | gameshark_write_byte_address = (current_fpos + 16); 1409 | bytes=(unsigned char *)&gameshark_write_byte_address; 1410 | bytes[3] = 0xD0; 1411 | printf("%08X 0004\n", gameshark_write_byte_address); 1412 | bytes=(unsigned char *)&gameshark_write_byte_address; 1413 | bytes[3] = 0x80; 1414 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1415 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1416 | 1417 | // offset 18 = 0x8014 -> 0x0000 1418 | gameshark_write_byte_address = (current_fpos + 18); 1419 | bytes=(unsigned char *)&gameshark_write_byte_address; 1420 | bytes[3] = 0xD0; 1421 | printf("%08X 1480\n", gameshark_write_byte_address); 1422 | bytes=(unsigned char *)&gameshark_write_byte_address; 1423 | bytes[3] = 0x80; 1424 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1425 | printf("%08X 0000\n\n", gameshark_write_byte_address); 1426 | } 1427 | 1428 | matched_libcrypt_2_magic_word = true; 1429 | for(int i=0; i < 4; i++) 1430 | { 1431 | if(libcrypt_2_magic_word[i] != buf[current_fpos + i]) 1432 | matched_libcrypt_2_magic_word = false; 1433 | } 1434 | 1435 | if(matched_libcrypt_2_magic_word) 1436 | { 1437 | // offset 0 = 0x2530 -> 1438 | gameshark_write_byte_address = (current_fpos); 1439 | bytes=(unsigned char *)&gameshark_write_byte_address; 1440 | bytes[3] = 0xD0; 1441 | printf("%08X 3025\n", gameshark_write_byte_address); 1442 | bytes=(unsigned char *)&gameshark_write_byte_address; 1443 | bytes[3] = 0x80; 1444 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1445 | printf("%08X %04X\n\n", gameshark_write_byte_address, magic_word); 1446 | 1447 | // offset 2 = 0x8600 -> 0xC634 1448 | gameshark_write_byte_address = (current_fpos + 2); 1449 | bytes=(unsigned char *)&gameshark_write_byte_address; 1450 | bytes[3] = 0xD0; 1451 | printf("%08X 0086\n", gameshark_write_byte_address); 1452 | bytes=(unsigned char *)&gameshark_write_byte_address; 1453 | bytes[3] = 0x80; 1454 | gameshark_write_byte_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1455 | printf("%08X 34C6\n\n", gameshark_write_byte_address); 1456 | 1457 | if(libcrypt_2_anti_pro_action_replay_offset != 0) 1458 | printf("Got LibCrypt v2 Anti-Pro Action Replay bypass (By B.A.D) match at offset: 0x%08X\n\n", libcrypt_2_anti_pro_action_replay_offset); 1459 | 1460 | if(libcrypt_2_anti_mod_chip_offset != 0) 1461 | printf("Got LibCrypt v2 Anti-Mod-Chip bypass (By B.A.D) match at offset: 0x%08X\n\n", libcrypt_2_anti_mod_chip_offset); 1462 | 1463 | printf("Got LibCrypt v2 Magic Word bypass (By B.A.D) match at offset: 0x%08X\n\n", current_fpos); 1464 | 1465 | break; // nothing else to do 1466 | } 1467 | } 1468 | current_fpos++; // Move to next byte to start checks all over again until EOF 1469 | } 1470 | 1471 | 1472 | free(mem_dump_1_buf); 1473 | fclose(mem_dump_1); 1474 | } 1475 | 1476 | void sharkconv(const char **argv) 1477 | { 1478 | unsigned int match_pattern_len = strtoul(argv[5], NULL, 0); 1479 | printf("Pattern Length: %d\n", match_pattern_len); 1480 | unsigned int gameshark_code_address = strtoul(argv[1], NULL, 16); 1481 | printf("Got old game ver gameshark code address %08X\n", gameshark_code_address); 1482 | bytes=(unsigned char *)&gameshark_code_address; 1483 | printf("Got old game ver gameshark code type prefix: %02X\n", bytes[3]); 1484 | gameshark_prefix = bytes[3]; 1485 | bytes[3] = 0x00; // the 80 or D0 is a prefix for the gameshark code type, the actual memory location is 00 in this part of the address 1486 | gameshark_code_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1487 | printf("Got old game RAM dump address %08X\n", gameshark_code_address); 1488 | // unsigned int base_gameshark_code_address = (gameshark_code_address/16); // rounds down, ignores any remainder 1489 | //base_gameshark_code_address = (base_gameshark_code_address * 16); // Boom we got the start of the even memory address that the write value is written into 1490 | //printf("Got old game ver gameshark code base address %08X\n", base_gameshark_code_address); 1491 | unsigned int base_to_write_val = (match_pattern_len - 2); // Offset to where the write value is written to from the base address 1492 | printf("Got old game ver gameshark code base address offset for write value: %X\n", base_to_write_val); 1493 | unsigned short gameshark_code_value = strtoul(argv[2], NULL, 16) & 0xFFFF; 1494 | printf("Got old game ver gameshark code value %04X\n", gameshark_code_value); 1495 | unsigned char gameshark_code_write_values[2]; 1496 | gameshark_code_write_values[0] = gameshark_code_value; 1497 | gameshark_code_write_values[1] = (gameshark_code_value>>8); 1498 | printf("Write byte 1 %X\n", gameshark_code_write_values[0]); 1499 | printf("Write byte 2 %X\n", gameshark_code_write_values[1]); 1500 | 1501 | if((mem_dump_1 = fopen(argv[3], "rb")) != NULL) 1502 | { 1503 | fseek(mem_dump_1, 0, SEEK_END); 1504 | mem_dump_1_size = ftell(mem_dump_1); 1505 | 1506 | if(mem_dump_1_size != valid_mem_dump_size) 1507 | { 1508 | printf("Error: the original game's RAM dump file: %s is not the expected size\n", argv[3]); 1509 | if(mem_dump_1_size == 8388608) 1510 | printf("Do you have the 'Enable 8MB RAM' option enabled? Uncheck that option if so and make a new RAM dump\n"); 1511 | fclose(mem_dump_1); 1512 | free(mem_dump_1_buf); 1513 | return; 1514 | } 1515 | 1516 | fseek(mem_dump_1, 0, SEEK_SET); 1517 | mem_dump_1_buf = (unsigned char *)malloc(mem_dump_1_size * sizeof(unsigned char)); 1518 | 1519 | if(fread(mem_dump_1_buf, 1, valid_mem_dump_size, mem_dump_1) != mem_dump_1_size) 1520 | { 1521 | printf("Error loading original game's RAM dump file: %s\n", argv[3]); 1522 | fclose(mem_dump_1); 1523 | free(mem_dump_1_buf); 1524 | return; 1525 | } 1526 | 1527 | printf("Loaded the original game's RAM dump file: %s (%d bytes in memory)\n", argv[3], mem_dump_1_size); 1528 | } else { 1529 | printf("Cannot open the original game's memory dump file: %s\n", argv[3]); 1530 | return; 1531 | } 1532 | 1533 | if((mem_dump_2 = fopen(argv[4], "rb")) != NULL) 1534 | { 1535 | fseek(mem_dump_2, 0, SEEK_END); 1536 | mem_dump_2_size = ftell(mem_dump_2); 1537 | 1538 | if(mem_dump_2_size != valid_mem_dump_size) 1539 | { 1540 | printf("Error: the new game ver RAM dump file: %s is not the expected size\n", argv[4]); 1541 | 1542 | if(mem_dump_2_size == 8388608) 1543 | printf("Do you have the 'Enable 8MB RAM' option enabled? Uncheck that option if so and make a new RAM dump\n"); 1544 | 1545 | fclose(mem_dump_1); 1546 | fclose(mem_dump_2); 1547 | free(mem_dump_1_buf); 1548 | free(mem_dump_2_buf); 1549 | return; 1550 | } 1551 | 1552 | fseek(mem_dump_2, 0, SEEK_SET); 1553 | mem_dump_2_buf = (unsigned char *)malloc(mem_dump_2_size * sizeof(unsigned char)); 1554 | 1555 | 1556 | if(fread(mem_dump_2_buf, 1, valid_mem_dump_size, mem_dump_2) != mem_dump_2_size) 1557 | { 1558 | fclose(mem_dump_1); 1559 | fclose(mem_dump_2); 1560 | free(mem_dump_1_buf); 1561 | free(mem_dump_2_buf); 1562 | return; 1563 | } 1564 | 1565 | printf("Loaded new game ver RAM dump file: %s (%d bytes in memory)\n", argv[4], mem_dump_2_size); 1566 | } else { 1567 | printf("Cannot open the new game ver RAM dump file: %s\n", argv[4]); 1568 | fclose(mem_dump_1); 1569 | free(mem_dump_1_buf); 1570 | return; 1571 | } 1572 | 1573 | unsigned char match_pattern[match_pattern_len]; 1574 | unsigned char pattern[match_pattern_len]; 1575 | get_start_of_pattern_pos = (gameshark_code_address - base_to_write_val); // Start at beginning of memory address gameshark code is modifying to identify pattern 1576 | 1577 | for(int i=0; i < match_pattern_len; i++) 1578 | { 1579 | pattern[i] = mem_dump_1_buf[get_start_of_pattern_pos + i]; 1580 | unsigned int current_address = (get_start_of_pattern_pos + i); 1581 | printf("%08X: %02X\n", current_address, pattern[i]); 1582 | } 1583 | 1584 | if(pattern[base_to_write_val] == gameshark_code_write_values[0]) 1585 | printf("Confirmed gameshark code write byte 1: %X in: %s\n", gameshark_code_write_values[0], argv[3]); 1586 | 1587 | if(pattern[base_to_write_val + 1] == gameshark_code_write_values[1]) 1588 | printf("Confirmed gameshark code write byte 2: %X in: %s\n", gameshark_code_write_values[1], argv[3]); 1589 | 1590 | for(current_fpos = 0; current_fpos < valid_mem_dump_size; current_fpos++) 1591 | { 1592 | if(find_first_different_byte) 1593 | { 1594 | if(current_fpos > 0x10000) // start of user RAM is 80010000 1595 | { 1596 | if(mem_dump_1_buf[current_fpos] != mem_dump_2_buf[current_fpos]) 1597 | { 1598 | printf("First different byte between revisions is %02X (old game ver) versus %02X (new game ver) at %08X\n", mem_dump_1_buf[current_fpos], mem_dump_2_buf[current_fpos], current_fpos); 1599 | find_first_different_byte = false; 1600 | } 1601 | } 1602 | } 1603 | 1604 | for(int i=0; i < match_pattern_len; i++) 1605 | match_pattern[i] = mem_dump_2_buf[current_fpos + i]; 1606 | 1607 | bool match = true; 1608 | for(int i=0; i < match_pattern_len; i++) 1609 | { 1610 | if(match_pattern[i] != pattern[i]) 1611 | match = false; 1612 | } 1613 | 1614 | if(match) 1615 | { 1616 | printf("\nGot pattern match in %s starting at base address: %08X\n", argv[4], current_fpos); 1617 | printf("Got new write val offset at address: %08X\n", current_fpos + base_to_write_val); 1618 | gameshark_code_address = (current_fpos + base_to_write_val); 1619 | bytes=(unsigned char *)&gameshark_code_address; 1620 | bytes[3] = gameshark_prefix; // the 80 or D0 is a prefix for the gameshark code type, the actual memory location is 00 in this part of the address 1621 | gameshark_code_address = bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24); // NOT BIG-ENDIAN SAFE 1622 | printf("Got new game ver gameshark code: %08X %02X%02X\n", gameshark_code_address, gameshark_code_write_values[1], gameshark_code_write_values[0]); 1623 | } 1624 | } 1625 | 1626 | fclose(mem_dump_1); 1627 | fclose(mem_dump_2); 1628 | free(mem_dump_1_buf); 1629 | free(mem_dump_2_buf); 1630 | } 1631 | 1632 | void sector_diff(const char **argv, int argc) 1633 | { 1634 | 1635 | // bin 1 1636 | fseek(bin, 0, SEEK_END); 1637 | bin_size = ftell(bin); 1638 | 1639 | if(bin_size > 0x2EE00000) // 750MB max, no PSX software comes even close to such a size 1640 | { 1641 | printf("Error: BIN file 1: %s exceeds the maximum filesize of 750MB in bin patch mode\n", argv[2]); 1642 | fclose(bin); 1643 | return; 1644 | } 1645 | 1646 | fseek(bin, 0, SEEK_SET); 1647 | buf = (unsigned char *)malloc(bin_size * sizeof(unsigned char)); // Read entire BIN to memory for performance gain, I mean it's 2022 who doesn't have a free ~700MBs of RAM?! 1648 | 1649 | if(fread(buf, 1, bin_size, bin) != bin_size) 1650 | { 1651 | printf("Error loading BIN file 1: %s\n", argv[2]); 1652 | return; 1653 | } 1654 | 1655 | printf("Successfully loaded BIN file 1: %s (%d bytes in memory)\n", argv[2], bin_size); 1656 | //max_size = bin_size; 1657 | number_of_sectors = (bin_size / 2352); 1658 | 1659 | // bin 2 1660 | fseek(bin_2, 0, SEEK_END); 1661 | bin_size_2 = ftell(bin_2); 1662 | 1663 | if(bin_size_2 > 0x2EE00000) // 750MB max, no PSX software comes even close to such a size 1664 | { 1665 | printf("Error: The BIN file 2: %s exceeds the maximum filesize of 750MB in bin patch mode\n", argv[3]); 1666 | fclose(bin_2); 1667 | return; 1668 | } 1669 | 1670 | fseek(bin_2, 0, SEEK_SET); 1671 | buf_2 = (unsigned char *)malloc(bin_size_2 * sizeof(unsigned char)); // Read entire BIN to memory for performance gain, I mean it's 2022 who doesn't have a free ~700MBs of RAM?! 1672 | 1673 | if(fread(buf_2, 1, bin_size_2, bin_2) != bin_size_2) 1674 | { 1675 | printf("Error loading BIN file 2: %s\n", argv[3]); 1676 | return; 1677 | } 1678 | 1679 | printf("Successfully loaded BIN file 2: %s (%d bytes in memory)\n", argv[3], bin_size_2); 1680 | number_of_sectors_2 = (bin_size_2 / 2352); 1681 | 1682 | // sanity checks 1683 | if(bin_size != bin_size_2) 1684 | { 1685 | printf("Info: size difference:\n%s : %d bytes (%d sectors)\n%s : %d bytes (%d sectors)\n", argv[2], bin_size, number_of_sectors, argv[3], bin_size_2, number_of_sectors_2); 1686 | if(bin_size > bin_size_2) 1687 | { 1688 | max_size = bin_size_2; 1689 | } else { 1690 | max_size = bin_size; 1691 | } 1692 | } else { 1693 | printf("\nInfo: %s and %s are both %d bytes (%d sectors)\n", argv[2], argv[3], bin_size, number_of_sectors); 1694 | max_size = bin_size; 1695 | } 1696 | 1697 | printf("\nScanning %d sectors, please wait...\n", number_of_sectors); 1698 | current_fpos = 0; 1699 | unsigned int previous_address = 0; 1700 | 1701 | while(1) 1702 | { 1703 | if(current_fpos > max_size) 1704 | break; // even number of sectors, done reading the file. 1705 | 1706 | if((current_fpos + 0x930) == max_size) // odd number of sectors 1707 | last_sector = 1; // This function is reading 2 sectors at a time, so if there is an odd number of sectors we have to change the behavior to only search the last sector. Explicitly break loop when this is set. 1708 | 1709 | // load bin 1 sector user data 1710 | for(int i=0; i < 0x800; i++) 1711 | { 1712 | sectors[i] = buf[current_fpos + i + 0x18]; // skip 0x18 header info per sector 1713 | } 1714 | 1715 | // load bin 2 sector user data 1716 | for(int i=0; i < 0x800; i++) 1717 | { 1718 | sectors_2[i] = buf_2[current_fpos + i + 0x18]; // skip 0x18 header info per sector 1719 | } 1720 | 1721 | search_size = 0x800; 1722 | 1723 | // compare sector user data 1724 | for(int s = 0; s < search_size; s++) 1725 | { 1726 | if(sectors[s] != sectors_2[s]) 1727 | { 1728 | if(find_first_different_byte) 1729 | { 1730 | printf("\n----------------------------\n| Offset | File 1 | File 2 |\n----------------------------\n"); 1731 | find_first_different_byte = false; 1732 | } 1733 | 1734 | //printf("Prev: %08X\n", previous_address); 1735 | 1736 | while ((previous_address != 0) && (previous_address != (current_fpos + s + 0x18 - 1)) && (previous_address < (current_fpos + s + 0x18))) 1737 | { 1738 | previous_address++; 1739 | printf("0x%08X 0x00 0x00\n", previous_address); // gen skip in array 1740 | if(argc == 5) 1741 | { 1742 | //fprintf(patch, "%08X 00 00\n", previous_address); 1743 | fprintf(patch, "00 00\n"); 1744 | } 1745 | } 1746 | previous_address = (current_fpos + s + 0x18); 1747 | printf("0x%08X 0x%02X 0x%02X\n", (current_fpos + s + 0x18), sectors[s], sectors_2[s]); 1748 | 1749 | if(argc == 5) 1750 | { 1751 | //fprintf(patch, "%08X %02X %02X\n", (current_fpos + s + 0x18), sectors[s], sectors_2[s]); 1752 | fprintf(patch, "%02X %02X\n", sectors[s], sectors_2[s]); 1753 | } 1754 | } 1755 | } 1756 | 1757 | if(last_sector) 1758 | { 1759 | break; // That was the last sector 1760 | } 1761 | 1762 | current_fpos = (current_fpos + 0x930); // Advance one sector. 1763 | } 1764 | 1765 | 1766 | if(argc == 5) 1767 | { 1768 | fclose(patch); 1769 | } 1770 | 1771 | fclose(bin); 1772 | fclose(bin_2); 1773 | free(buf); 1774 | free(buf_2); 1775 | } 1776 | 1777 | int main (int argc, const char * argv[]) 1778 | { 1779 | printf("APrip %s By Alex Free (C)2022-2024 (3-BSD)\nhttps://alex-free.github.io/aprip\n\n", VERSION); 1780 | 1781 | if(argc == 3) 1782 | { 1783 | if((strcmp("-b", argv[1])) == 0) { 1784 | printf("Mode: CD image BIN patcher for APv1 or APv2\n\n"); 1785 | if((bin = fopen(argv[2], "rb+")) != NULL) 1786 | { 1787 | bin_patch(argv); 1788 | } else { 1789 | printf("Error: Cannot open the BIN file: %s\n", argv[2]); 1790 | return(1); 1791 | } 1792 | } else if((strcmp("-gs", argv[1])) == 0) { 1793 | printf("Mode: GameShark code generation for APv1 or APv2\n\n"); 1794 | if((mem_dump_1 = fopen(argv[2], "rb")) != NULL) 1795 | { 1796 | gameshark_gen(argv); 1797 | } else { 1798 | printf("Error: Cannot open the RAM dump file: %s\n", argv[2]); 1799 | return(1); 1800 | } 1801 | } else { 1802 | printf("Error: The first argument should be either -gs or -b if you want to use a 2 argument function\n"); 1803 | return(1); 1804 | } 1805 | } else if (argc == 4) { 1806 | if((strcmp("-b", argv[1])) == 0) { 1807 | printf("Mode: CD image BIN patcher for LibCrypt v1 or LibCrypt v2\n\n"); 1808 | if((bin = fopen(argv[3], "rb+")) != NULL) 1809 | { 1810 | bin_patch_libcrypt(argv); 1811 | } else { 1812 | printf("Error: Cannot open the BIN file: %s\n", argv[3]); 1813 | return(1); 1814 | } 1815 | } else if((strcmp("-d", argv[1])) == 0) { 1816 | printf("Mode: Sector user data compare\n\n"); 1817 | if((bin = fopen(argv[2], "rb")) == NULL) 1818 | { 1819 | printf("Error: Cannot open bin file 1: %s\n", argv[2]); 1820 | return(1); 1821 | } 1822 | if((bin_2 = fopen(argv[3], "rb")) == NULL) 1823 | { 1824 | printf("Error: Cannot open bin file 2: %s\n", argv[3]); 1825 | return(1); 1826 | } else { 1827 | sector_diff(argv, argc); 1828 | } 1829 | 1830 | } else if((strcmp("-p", argv[1])) == 0) { 1831 | printf("Mode: Sector user data compare patch applicator\n\n"); 1832 | if((bin = fopen(argv[2], "rb+")) == NULL) 1833 | { 1834 | printf("Error: Cannot open bin file: %s\n", argv[2]); 1835 | return(1); 1836 | } 1837 | if((patch = fopen(argv[3], "r")) == NULL) 1838 | { 1839 | printf("Error: Cannot open patch file: %s\n", argv[3]); 1840 | return(1); 1841 | } else { 1842 | bin_patch_custom(argv); 1843 | } 1844 | } else if((strcmp("-gs", argv[1])) == 0) { 1845 | printf("Mode: GameShark code generation for LibCrypt v1 or LibCrypt v2\n\n"); 1846 | libcrypt = true; 1847 | magic_word = strtoul(argv[3], NULL, 16); 1848 | printf("Magic Word: %08X\n", magic_word); 1849 | //unsigned char *bytes; 1850 | //bytes=(unsigned char *)&magic_word; 1851 | /* 1852 | printf("Bytes 0: %02X\n", bytes[0]); // AA 1853 | printf("Bytes 1: %02X\n", bytes[1]); // 87 1854 | printf("Bytes 2: %02X\n", bytes[2]); 1855 | printf("Bytes 3: %02X\n", bytes[3]); 1856 | */ 1857 | if((mem_dump_1 = fopen(argv[2], "rb")) != NULL) 1858 | { 1859 | gameshark_gen(argv); 1860 | } else { 1861 | printf("Error: Cannot open the RAM dump file: %s\n", argv[2]); 1862 | return(1); 1863 | } 1864 | } 1865 | } else if (argc == 5) { 1866 | if((strcmp("-d", argv[1])) == 0) { 1867 | printf("Mode: Sector user data compare with patch generation\n\n"); 1868 | if((bin = fopen(argv[2], "rb")) == NULL) 1869 | { 1870 | printf("Error: Cannot open bin file 1: %s\n", argv[2]); 1871 | return(1); 1872 | } 1873 | if((bin_2 = fopen(argv[3], "rb")) == NULL) 1874 | { 1875 | printf("Error: Cannot open bin file 2: %s\n", argv[3]); 1876 | return(1); 1877 | } 1878 | if((patch = fopen(argv[4], "wb+")) == NULL) 1879 | { 1880 | printf("Error: Cannot open patch file: %s\n", argv[4]); 1881 | return(1); 1882 | } else { 1883 | sector_diff(argv, argc); 1884 | } 1885 | } 1886 | } else if (argc == 6) { 1887 | printf("Mode: GameShark code converter\n"); 1888 | sharkconv(argv); 1889 | } else { 1890 | printf("Error: Incorrect number of arguments\n" 1891 | "Usage:\n\n" 1892 | "*****CD Image Features*****\n\n" 1893 | "Patch CD BIN file containing APv1 or APv2 protection:\n" 1894 | "aprip -b \n(Patch BIN file directly)\n\n" 1895 | 1896 | "Patch CD BIN file containg LibCrypt v1 or LibCrypt v2 protection:\n" 1897 | "aprip -b \n\n" 1898 | 1899 | "Compare sector user data of 2 CD BIN files\n" 1900 | "aprip -d \n\n" 1901 | 1902 | "Compare sector user data of 2 CD BIN files and generate aprip-style patch\n" 1903 | "aprip -d \n\n" 1904 | 1905 | "Patch sector user data of a CD BIN file with an aprip-style patch\n" 1906 | "aprip -p \n\n" 1907 | 1908 | "*****RAM Dump Features*****\n\n" 1909 | "Create gameshark codes to bypass APv1 or APv2 protection:\n" 1910 | "aprip -gs \n\n" 1911 | 1912 | "Create GameShark codes to bypass LibCrypt v1 or LibCrypt v2 protection:\n" 1913 | "aprip -gs \n\n" 1914 | 1915 | "Convert an existing GameShark code for a specific game disc to a different revision of the same game disc, or another title of the same series of the game:\n" 1916 | 1917 | "aprip \n"); 1918 | 1919 | return(1); 1920 | } 1921 | } -------------------------------------------------------------------------------- /build.md: -------------------------------------------------------------------------------- 1 | 2 | # Building From Source 3 | 4 | In the source directory, you may execute any of the following: 5 | 6 | `make deps-apt` - installs the build dependencies required to compile the program. 7 | 8 | `make` - creates an executable for x86_64 Linux. 9 | 10 | `make clean` - deletes only the generated executable file created by only executing `make`. 11 | 12 | `make clean-build` - deletes the generated build directory in it's entirety. 13 | 14 | `make all` - **generate all of the following:** 15 | 16 | ### For Windows 95 OSR 2.5 and above, Pentium CPU minimum (32 bit) 17 | 18 | * Windows i686 static executable file 19 | * Portable Windows i386 release .zip file 20 | 21 | ### For Windows x86_64 (64 bit) 22 | 23 | * Windows x86_64 static executable file 24 | * Portable Windows x86_64 release .zip file 25 | 26 | ### For Linux 3.2.0 and above, 386 CPU minimum (32 bit) 27 | 28 | * Linux i386 static executable file 29 | * Portable Linux i386 release .zip file 30 | * Linux i386 release .deb file 31 | 32 | ### For Linux 3.2.0 and above, x86_64 (64 bit) 33 | 34 | * Linux x86_64 static executable file 35 | * Portable Linux x86_64 release .zip file 36 | * Linux x86_64 release .deb file 37 | 38 | All output is found in the build directory created in the source directory. -------------------------------------------------------------------------------- /cd-command-logger.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | echo DuckStation CD Command Logger v1.0.1 For Windows 4 | echo By Alex Free 5 | set argC=0 6 | for %%x in (%*) do Set /A argC+=1 7 | 8 | IF NOT "%argC%" == "2" ( 9 | echo Error: Incorrect number of arguments given to %0%. 10 | echo Usage: 11 | echo %0% ^ ^ 12 | echo. 13 | cmd /k 14 | ) 15 | 16 | IF NOT EXIST "%~f1" ( 17 | echo Error: Can't open the DuckStation executable file: "%~f1" 18 | echo. 19 | cmd /k 20 | ) 21 | 22 | IF NOT EXIST "%~f2" ( 23 | echo Error: Can't open the PSX game cue file: "%~f2" 24 | echo. 25 | cmd /k 26 | ) 27 | 28 | FOR /F "tokens=3 delims= " %%G IN ('REG QUERY "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal"') DO (SET docsdir=%%G) 29 | set log="%docsdir%\DuckStation\duckstation.log" 30 | del %log% 2> nul 31 | "%~f1" "%~f2" 32 | echo. 33 | %SystemRoot%\system32\find "CDROM executing command" %log% 34 | echo. 35 | echo Number Of CD Commands Sent: 36 | %SystemRoot%\system32\find /C "CDROM executing command" %log% -------------------------------------------------------------------------------- /cd-command-logger.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "DuckStation CD Command Logger v1.0.1 For Linux\nBy Alex Free\n" 4 | 5 | if [ $# -ne 2 ]; then 6 | echo -e "Error: Incorrect number of arguments.\nUsage:\n \n" 7 | exit 1 8 | fi 9 | 10 | if [ ! -f "$1" ]; then 11 | echo -e "Error: Can't open the DuckStation executable file: "$1"\n" 12 | exit 1 13 | fi 14 | 15 | if [ ! -f "$2" ]; then 16 | echo -e "Error: Can't open the PSX game cue file: "$2"\n" 17 | exit 1 18 | fi 19 | 20 | log="$HOME/.local/share/duckstation/duckstation.log" 21 | rm -f "$log" 22 | "$1" "$2" 23 | 24 | grep -r "CDROM executing command" "$log" 25 | echo 26 | echo "Number Of CD Commands Sent:" 27 | grep -rc "CDROM executing command" "$log" -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # [Aprip](readme.md) -> Changelog 2 | 3 | ## Version 1.0.8 (5/28/2024) 4 | 5 | Changes: 6 | 7 | * Implemented [sector user data compare function](#comparing-cd-images). This allows you to display only differences in user data (and not ECC/EDC) between i.e. an unmodified disc image vs a patched one. 8 | 9 | * Implemented [Aprip-style patch creation/applicator functions](#creating-aprip-style-patches). An APrip-style patch searches all sector user data for a pattern of bytes and then patches specific bytes in the pattern (pre-defined in the patch) without using known offsets. It can be used to 'port' patch methods. 10 | 11 | ---------------------------------------------------- 12 | 13 | * [aprip-v1.0.8-windows-i686-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.8/aprip-v1.0.8-windows-i686-static.zip) _Portable Release For Windows 95 OSR 2.5 and above, Pentium CPU minimum (32 bit)_ 14 | 15 | * [aprip-v1.0.8-windows-x86\_64-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.8/aprip-v1.0.8-windows-x86_64-static.zip) _Portable Release For x86_64 Windows (64 bit)_ 16 | 17 | * [aprip-v1.0.8-linux-i386-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.8/aprip-v1.0.8-linux-i386-static.zip) _Portable Release For Linux 3.2.0 and above, 386 CPU minimum (32 bit)_ 18 | 19 | * [aprip-v1.0.8-linux-i386-static.deb](https://github.com/alex-free/aprip/releases/download/v1.0.8/aprip-v1.0.8-linux-i386-static.deb) _Deb package file For Linux 3.2.0 and above, 386 CPU minimum (32 bit)_ 20 | 21 | * [aprip-v1.0.8-linux-x86\_64-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.8/aprip-v1.0.8-linux-x86_64-static.zip) _Portable Release For x86\_64 Linux 3.2.0 and above (64 bit)_ 22 | 23 | * [aprip-v1.0.8-linux-x86\_64-static.deb](https://github.com/alex-free/aprip/releases/download/v1.0.8/aprip-v1.0.8-linux-x86_64-static.deb) _Deb package file for x86_64 Linux 3.2.0 and above (64 bit)_ 24 | 25 | --------------------------------------- 26 | 27 | ## Version 1.0.7 (4/2/2024) 28 | 29 | Changes: 30 | 31 | * Implemented Append No Swap Bypass (By mdmdj) for disc image patcher mode. This only currently supports the [Append Discs](https://remywiki.com/APPEND_DISC) [Beat Mania Append GottaMix](http://redump.org/disc/3550/) and [Beat Mania Append 3rdMix](http://redump.org/disc/2306/), allowing them to work without a [Key Disc](https://remywiki.com/KEY_DISC). 32 | 33 | * Implemented my new [EzRe](https://github.com/alex-free/ezre) build system. 34 | 35 | ---------------------------------------------------- 36 | 37 | * [aprip-v1.0.7-windows-i686-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.7/aprip-v1.0.7-windows-i686-static.zip) _Portable Release For Windows 95 OSR 2.5 and above, Pentium CPU minimum (32 bit)_ 38 | 39 | * [aprip-v1.0.7-windows-x86\_64-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.7/aprip-v1.0.7-windows-x86_64-static.zip) _Portable Release For x86_64 Windows (64 bit)_ 40 | 41 | * [aprip-v1.0.7-linux-i386-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.7/aprip-v1.0.7-linux-i386-static.zip) _Portable Release For Linux 3.2.0 and above, 386 CPU minimum (32 bit)_ 42 | 43 | * [aprip-v1.0.7-linux-i386-static.deb](https://github.com/alex-free/aprip/releases/download/v1.0.7/aprip-v1.0.7-linux-i386-static.deb) _Deb package file For Linux 3.2.0 and above, 386 CPU minimum (32 bit)_ 44 | 45 | * [aprip-v1.0.7-linux-x86\_64-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.7/aprip-v1.0.7-linux-x86_64-static.zip) _Portable Release For x86\_64 Linux 3.2.0 and above (64 bit)_ 46 | 47 | * [aprip-v1.0.7-linux-x86\_64-static.deb](https://github.com/alex-free/aprip/releases/download/v1.0.7/aprip-v1.0.7-linux-x86_64-static.deb) _Deb package file for x86_64 Linux 3.2.0 and above (64 bit)_ 48 | 49 | --------------------------------------- 50 | 51 | ## Version 1.0.6 (11/6/2023) 52 | 53 | * [aprip-v1.0.6-windows-x86](https://github.com/alex-free/aprip/releases/download/v1.0.6/aprip-v1.0.6-windows-x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 54 | * [aprip-v1.0.6-windows-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.6/aprip-v1.0.6-windows-x86_64.zip) _For 64-bit Windows_ 55 | * [aprip-v1.0.6-linux-x86](https://github.com/alex-free/aprip/releases/download/v1.0.6/aprip-v1.0.6-linux-x86_static.zip) _For x86 Linux Distros_ 56 | * [aprip-v1.0.6-linux-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.6/aprip-v1.0.6-linux-x86_64_static.zip) _For x86\_64 Linux Distros_ 57 | * [aprip-1.0.6-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.6.zip) 58 | 59 | Changes: 60 | 61 | * Added support for my Zero APv1 bypass for GameShark code generation and CD image BIN file patching. 62 | 63 | * Added support for the Fake PAL APv2 bypass for GameShark code generation and CD image BIN file patching. 64 | 65 | * Added support for B.A.D's LibCrypt v2 bypass for GameShark code generation and CD image BIN file patching. 66 | 67 | ## Version 1.0.5 (6/26/2023) 68 | 69 | * [aprip-1.0.5-windows\_x86](https://github.com/alex-free/aprip/releases/download/v1.0.5/aprip-1.0.5-windows_x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 70 | * [aprip-1.0.5-windows\_x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.5/aprip-1.0.5-windows_x86_64.zip) _For 64-bit Windows_ 71 | * [aprip-1.0.5-linux\_x86](https://github.com/alex-free/aprip/releases/download/v1.0.5/aprip-1.0.5-linux_x86_static.zip) _For x86 Linux Distros_ 72 | * [aprip-1.0.5-linux\_x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.5/aprip-1.0.5-linux_x86_64_static.zip) _For x86_64 Linux Distros_ 73 | * [aprip-1.0.5-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.5.zip) 74 | 75 | Changes: 76 | 77 | * Removed broken non-stealth mod-chip APv1/APv2 patching. 78 | 79 | * Improved and cleaned up code, enabling build on Pop!OS. 80 | 81 | * Added info on [EDC](#edc) Protected games. 82 | 83 | ## Version 1.0.4 84 | 85 | * [aprip-1.0.4-windows\_x86](https://github.com/alex-free/aprip/releases/download/v1.0.4/aprip-1.0.4-windows_x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 86 | * [aprip-1.0.4-windows-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.4/aprip-1.0.4-windows_x86_64.zip) _For 64-bit Windows_ 87 | * [aprip-1.0.4-linux-x86](https://github.com/alex-free/aprip/releases/download/v1.0.4/aprip-1.0.4-linux_x86_static.zip) _For x86 Linux Distros_ 88 | * [aprip-1.0.4-linux-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.4/aprip-1.0.4-linux_x86_64_static.zip) _For x86_64 Linux Distros_ 89 | * [aprip-1.0.4-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.4.zip) 90 | 91 | Changes: 92 | 93 | * Added support for LibCrypt v1 CD image patching (all the PAL regional releases of MediEvil now work)! 94 | 95 | ## Version 1.0.3 (6/22/2023) 96 | 97 | * [aprip-1.0.3-windows-x86](https://github.com/alex-free/aprip/releases/download/v1.0.3/aprip-1.0.3-windows_x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 98 | * [aprip-1.0.3-windows-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.3/aprip-1.0.3-windows_x86_64.zip) _For 64-bit Windows_ 99 | * [aprip-1.0.3-linux-x86](https://github.com/alex-free/aprip/releases/download/v1.0.3/aprip-1.0.3-linux_x86_static.zip) _For x86 Linux Distros_ 100 | * [aprip-1.0.3-linux-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.3/aprip-1.0.3-linux_x86_64_static.zip) _For x86_64 Linux Distros_ 101 | * [aprip-1.0.3-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.3.zip) 102 | 103 | Changes: 104 | 105 | * Added support for LibCrypt 2 CD image patching. 106 | 107 | * Substantial documentation rewrite. 108 | 109 | ## Version 1.0.2 (11/28/2022) 110 | * [aprip-1.0.2-windows-x86](https://github.com/alex-free/aprip/releases/download/v1.0.2/aprip-1.0.2-windows_x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 111 | * [aprip-1.0.2-windows-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.2/aprip-1.0.2-windows_x86_64.zip) _For 64-bit Windows_ 112 | * [aprip-1.0.2-linux-x86](https://github.com/alex-free/aprip/releases/download/v1.0.2/aprip-1.0.2-linux_x86_static.zip) _For x86 Linux Distros_ 113 | * [aprip-1.0.2-linux-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.2/aprip-1.0.2-linux_x86_64_static.zip) _For x86_64 Linux Distros_ 114 | * [aprip-1.0.2-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.2.zip) 115 | 116 | Changes: 117 | 118 | * Cleaned up `cd-command-logger.sh`/`cd-command-logger.bat` and `ap-type-checker.sh`/`ap-type-checker.bat` scripts. The Windows `find` command is now executed via it's full filepath (in system32) to ensure the correct find command is being executed. It was possible in v1.0.1 that some non-standard `%PATH%` configuration could override the Windows default `find` command, breaking the `.bat` scripts. 119 | * Small performance increase (start searching at sector 19 instead of 0) for the bin-patcher mode has been applied. 120 | 121 | ## Version 1.0.1 (11/8/2022) 122 | * [aprip-1.0.1-windows-x86](https://github.com/alex-free/aprip/releases/download/v1.0.1/aprip-1.0.1-windows_x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 123 | * [aprip-1.0.1-windows-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.1/aprip-1.0.1-windows_x86_64.zip) _For 64-bit Windows_ 124 | * [aprip-1.0.1-linux-x86](https://github.com/alex-free/aprip/releases/download/v1.0.1/aprip-1.0.1-linux_x86_static.zip) _For x86 Linux Distros_ 125 | * [aprip-1.0.1-linux-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0.1/aprip-1.0.1-linux_x86_64_static.zip) _For x86_64 Linux Distros_ 126 | * [aprip-1.0.1-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.1.zip) 127 | 128 | Changes: 129 | 130 | * Limited file size max for bin patch mode to 750MB (sanity check, no actual PSX software is even close to this). 131 | 132 | * Added info on DuckStation 8 MB RAM setting (must be left unchecked). APrip also warns you if you try to give it an 8MB RAM dump now after the filesize mismatch error explaining what happened. 133 | 134 | ### Version 1.0 (11/4/2022) 135 | * [aprip-1.0-windows-x86](https://github.com/alex-free/aprip/releases/download/v1.0/aprip-1.0-windows_x86.zip) _For Windows 95 OSR 2.5 Or Newer (32-bit Windows)_ 136 | * [aprip-1.0-windows-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0/aprip-1.0-windows_x86_64.zip) _For 64-bit Windows_ 137 | * [aprip-1.0-linux-x86](https://github.com/alex-free/aprip/releases/download/v1.0/aprip-1.0-linux_x86_static.zip) _For x86 Linux Distros_ 138 | * [aprip-1.0-linux-x86\_64](https://github.com/alex-free/aprip/releases/download/v1.0/aprip-1.0-linux_x86_64_static.zip) _For x86_64 Linux Distros_ 139 | * [aprip-1.0-source](https://github.com/alex-free/aprip/archive/refs/tags/v1.0.zip) -------------------------------------------------------------------------------- /control-i386: -------------------------------------------------------------------------------- 1 | Package: aprip 2 | Version: 1.0.9 3 | Maintainer: Alex Free 4 | Architecture: i386 5 | Homepage: https://alex-free.github.io/aprip 6 | Description: PSX Anti-Piracy Protection Bypass Generator. 7 | -------------------------------------------------------------------------------- /control-x86_64: -------------------------------------------------------------------------------- 1 | Package: aprip 2 | Version: 1.0.9 3 | Maintainer: Alex Free 4 | Architecture: amd64 5 | Homepage: https://alex-free.github.io/aprip 6 | Description: PSX Anti-Piracy Protection Bypass Generator. 7 | -------------------------------------------------------------------------------- /icepick-patch-re.txt: -------------------------------------------------------------------------------- 1 | MediEvil France ICEPICK patch (TRSI) reversing 2 | 3 | hexdiff 4 | 5 | Offset Old New Byte at 47 byte match to edit 6 | 1B2D2A74 0A 00 0 7 | 1B2D2A75 1 skip 8 | 1B2D2A76 80 00 2 9 | 1B2D2A77 14 00 3 10 | 1B2D2A78 4 skip 11 | 1B2D2A79 5 skip 12 | 1B2D2A7A 6 skip 13 | 1B2D2A7B 7 skip 14 | 1B2D2A7C 8 skip 15 | 1B2D2A7D 9 skip 16 | 1B2D2A7E A3 00 10 17 | 1B2D2A7F 90 00 11 18 | 1B2D2A80 02 00 12 19 | 1B2D2A81 13 skip 20 | 1B2D2A82 02 00 14 21 | 1B2D2A83 24 00 15 22 | 1B2D2A84 06 00 16 23 | 1B2D2A85 17 skip 24 | 1B2D2A86 62 00 18 25 | 1B2D2A87 14 00 19 26 | 1B2D2A88 0E 00 20 27 | 1B2D2A89 80 00 21 28 | 1B2D2A8A 03 00 22 29 | 1B2D2A8B 3C 00 23 30 | 1B2D2A8C 04 00 24 31 | 1B2D2A8D 25 skip 32 | 1B2D2A8E A3 00 26 33 | 1B2D2A8F 90 00 27 34 | 1B2D2A90 53 00 28 35 | 1B2D2A91 29 skip 36 | 1B2D2A92 02 00 30 37 | 1B2D2A93 24 00 31 38 | 1B2D2A94 02 00 32 39 | 1B2D2A95 33 skip 40 | 1B2D2A96 62 00 34 41 | 1B2D2A97 14 00 35 42 | 1B2D2A98 0E 00 36 43 | 1B2D2A99 80 00 37 44 | 1B2D2A9A 03 00 38 45 | 1B2D2A9B 3C 00 39 46 | 1B2D2A9C 07 00 40 47 | 1B2D2A9D 41 skip 48 | 1B2D2A9E A4 00 42 49 | 1B2D2A9F 90 00 43 50 | 1B2D2AA0 00 6A 44 Magic Word byte 2 51 | 1B2D2AA1 00 D1 45 Magic Word byte 1 (D16A = Magic Word) 52 | 1B2D2AA2 00 04 46 53 | 1B2D2AA3 00 24 47 (0x2F byte match) -------------------------------------------------------------------------------- /images/ap-type-checker-biohazard3jdemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-biohazard3jdemo.png -------------------------------------------------------------------------------- /images/ap-type-checker-biohazard3jr0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-biohazard3jr0.png -------------------------------------------------------------------------------- /images/ap-type-checker-dino-crisis-usa-rev-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-dino-crisis-usa-rev-0.png -------------------------------------------------------------------------------- /images/ap-type-checker-poporogue-japan-rev-0-dump-ram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-poporogue-japan-rev-0-dump-ram.png -------------------------------------------------------------------------------- /images/ap-type-checker-poporogue-japan-rev-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-poporogue-japan-rev-0.png -------------------------------------------------------------------------------- /images/ap-type-checker-resident-evil-3-germany.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-resident-evil-3-germany.png -------------------------------------------------------------------------------- /images/ap-type-checker-resident-evil-3-japan-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-resident-evil-3-japan-demo.png -------------------------------------------------------------------------------- /images/ap-type-checker-resident-evil-3-japan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ap-type-checker-resident-evil-3-japan.png -------------------------------------------------------------------------------- /images/ape-escape-europe-disc-image-patched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/ape-escape-europe-disc-image-patched.png -------------------------------------------------------------------------------- /images/dino-crisis-2-japan-disc-image-patched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/dino-crisis-2-japan-disc-image-patched.png -------------------------------------------------------------------------------- /images/dino-crisis-europe-lc2-patched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/dino-crisis-europe-lc2-patched.png -------------------------------------------------------------------------------- /images/dino-crisis-usa-rev-0-code-generation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/dino-crisis-usa-rev-0-code-generation.png -------------------------------------------------------------------------------- /images/dino-crisis-usa-rev-0-non-stealth-mod-chip-apv2-triggered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/dino-crisis-usa-rev-0-non-stealth-mod-chip-apv2-triggered.png -------------------------------------------------------------------------------- /images/dino-crisis-usa-rev-0-ram-dump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/dino-crisis-usa-rev-0-ram-dump.png -------------------------------------------------------------------------------- /images/dinocrisis2j-patched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/dinocrisis2j-patched.png -------------------------------------------------------------------------------- /images/duckstation-bios-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-bios-settings.png -------------------------------------------------------------------------------- /images/duckstation-changed-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-changed-settings.png -------------------------------------------------------------------------------- /images/duckstation-confirm-no-sbi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-confirm-no-sbi.png -------------------------------------------------------------------------------- /images/duckstation-default-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-default-settings.png -------------------------------------------------------------------------------- /images/duckstation-open-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-open-settings.png -------------------------------------------------------------------------------- /images/duckstation-ram-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-ram-settings.png -------------------------------------------------------------------------------- /images/duckstation-save-state-setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/duckstation-save-state-setting.png -------------------------------------------------------------------------------- /images/medievil-europe-lc1-screen.png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/medievil-europe-lc1-screen.png.png -------------------------------------------------------------------------------- /images/medievil-france-disc-image-patched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/medievil-france-disc-image-patched.png -------------------------------------------------------------------------------- /images/parasite-eve-2-europe-disc-1-cd-image-patched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/parasite-eve-2-europe-disc-1-cd-image-patched.png -------------------------------------------------------------------------------- /images/poporogue-japan-rev-0-non-stealth-mod-chip-apv1-triggered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/poporogue-japan-rev-0-non-stealth-mod-chip-apv1-triggered.png -------------------------------------------------------------------------------- /images/resident-evil-3-germany-code-generation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/resident-evil-3-germany-code-generation.png -------------------------------------------------------------------------------- /images/resident-evil-3-germany-dump-ram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/resident-evil-3-germany-dump-ram.png -------------------------------------------------------------------------------- /images/resident-evil-3-japan-demo-dump-ram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/resident-evil-3-japan-demo-dump-ram.png -------------------------------------------------------------------------------- /images/resident-evil-3-japan-dump-ram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/resident-evil-3-japan-dump-ram.png -------------------------------------------------------------------------------- /images/resident-evil-3-japan-rev-0-dump-ram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/resident-evil-3-japan-rev-0-dump-ram.png -------------------------------------------------------------------------------- /images/resident-evil-3-japan-to-japan-demo-conversion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alex-free/aprip/767fa1ded63076e2380822986120170272420443/images/resident-evil-3-japan-to-japan-demo-conversion.png -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # BSD 3-Clause License 2 | 3 | Copyright (c) 2022-2024 Alex Free 4 | All rights reserved. 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of the copyright holder nor the names of its 13 | contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # APrip: PSX Anti-Piracy Protection Bypass Generator 2 | 3 | _By Alex Free_ 4 | 5 | APrip Bypasses various anti-piracy schemes found in later PSX games with CD image generic patch finder and GameShark code generator functions. It can also convert existing GameShark bypass codes to different revisions of the same game, or a different game in the same series. 6 | 7 | It is used as a development aid for the Tonyhax International [anti-piracy bypass system](https://alex-free.github.io/tonyhax-international/anti-piracy-bypass.html), however it includes many features that may be of use to others. 8 | 9 | ## Table Of Contents 10 | 11 | * [Downloads](#downloads) 12 | * [Anti-Piracy Implementations](#anti-piracy-implementations) 13 | * [Patching The CD Image](#patching-the-cd-image) 14 | * [Generating GameShark Codes](#generating-gameshark-codes) 15 | * [Converting An Existing GameShark Codes](#converting-an-existing-gameshark-code). 16 | * [Comparing CD Images](#comparing-cd-images). 17 | * [Creating APrip-Style Patches](#creating-aprip-style-patches). 18 | * [Soft-Mods](#soft-mod) 19 | * [How It Works](#how-it-works) 20 | * [License](#license) 21 | * [Credits](#credits) 22 | 23 | ## Links 24 | 25 | * [Homepage](https://alex-free.github.io/aprip) 26 | * [Github](https://github.com/alex-free/aprip) 27 | * [Tonyhax International](https://alex-free.github.io/tonyhax-international) 28 | * [Tonyhax International APv2 Bypass System](https://alex-free.github.io/tonyhax-international/anti-piracy-bypass.html) 29 | * [FF VIII APv1 Reversing](https://consolecopyworld.com/psx/psx_ff8_protection.shtml) 30 | * [MediEvil LibCrypt v1 Reversing](https://consolecopyworld.com/psx/psx_medievil.shtml) 31 | * [LibCrypt PS1 Protection Bible](https://red-j.github.io/Libcrypt-PS1-Protection-bible/index.htm) 32 | * [APv2 Decomp/Reversing By Socram8888](https://github.com/socram8888/tonyhax/blob/master/docs/ap_v2.c) 33 | 34 | ## Downloads 35 | 36 | ### Version 1.0.9 (5/29/2024) 37 | 38 | Changes: 39 | 40 | * Add size limit verification and padding bug fix [Aprip-style patch creation/applicator functions](#creating-aprip-style-patches). 41 | 42 | ---------------------------------------------------- 43 | 44 | * [aprip-v1.0.9-windows-i686-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.9/aprip-v1.0.9-windows-i686-static.zip) _Portable Release For Windows 95 OSR 2.5 and above, Pentium CPU minimum (32 bit)_ 45 | 46 | * [aprip-v1.0.9-windows-x86\_64-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.9/aprip-v1.0.9-windows-x86_64-static.zip) _Portable Release For x86_64 Windows (64 bit)_ 47 | 48 | * [aprip-v1.0.9-linux-i386-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.9/aprip-v1.0.9-linux-i386-static.zip) _Portable Release For Linux 3.2.0 and above, 386 CPU minimum (32 bit)_ 49 | 50 | * [aprip-v1.0.9-linux-i386-static.deb](https://github.com/alex-free/aprip/releases/download/v1.0.9/aprip-v1.0.9-linux-i386-static.deb) _Deb package file For Linux 3.2.0 and above, 386 CPU minimum (32 bit)_ 51 | 52 | * [aprip-v1.0.9-linux-x86\_64-static.zip](https://github.com/alex-free/aprip/releases/download/v1.0.9/aprip-v1.0.9-linux-x86_64-static.zip) _Portable Release For x86\_64 Linux 3.2.0 and above (64 bit)_ 53 | 54 | * [aprip-v1.0.9-linux-x86\_64-static.deb](https://github.com/alex-free/aprip/releases/download/v1.0.9/aprip-v1.0.9-linux-x86_64-static.deb) _Deb package file for x86_64 Linux 3.2.0 and above (64 bit)_ 55 | 56 | --------------------------------------- 57 | 58 | [About Previous Versions](changelog.md). 59 | 60 | ## Anti-Piracy Implementations 61 | 62 | Starting in late 1998, some specific PSX games began implementing additional copy protection designed to prevent playing backup CD-R copies or import discs of said game. Different types of additional copy protection have been found in PSX games over the years. Below is more info on the 4 different anti-piracy protection schemes aprip currently supports: 63 | 64 | ### APv1 65 | 66 | The very first kind of additional anti-piracy copy protection that appeared was in the Japanese game [PoPoRogue](http://redump.org/disc/1552/). It is only found in various Japanese PSX games, no other region saw any game release with it. 67 | 68 | Trips non-stealth mod-chip consoles: Yes. 69 | Trips Unmodified soft-modded consoles: No. 70 | APrip bypasses For APv1: [Zero Bypass](#zero-bypass). 71 | 72 | ![PoPoRogue Japan Rev 0 APv1 Screen](images/poporogue-japan-rev-0-non-stealth-mod-chip-apv1-triggered.png) 73 | 74 | ### APv2 75 | 76 | This protection was first seen in the japanese release of [Dino Crisis](http://redump.org/disc/1598/). It is found in various PSX games spanning every region, not just in Japan like the original APv1 protection was limited to. 77 | 78 | Trips non-stealth mod-chip consoles: Yes. 79 | Trips Unmodified soft-modded consoles: Yes. 80 | APrip bypasses for APv2: [Fake PAL BIOS](#fake-pal-bypass) and [Fake VC0](#fake-vc0). 81 | 82 | ![Dino Crisis USA Rev 0 APv2 Screen](images/dino-crisis-usa-rev-0-non-stealth-mod-chip-apv2-triggered.png) 83 | 84 | ### LibCrypt v1 85 | 86 | The first version of LibCrypt (which prevents backup CD-Rs made with most standard burning programs from working), only found in the PAL region releases of MediEvil, listed below: 87 | 88 | * [MediEvil Europe](http://redump.org/disc/592/) 89 | * [MediEvil France](http://redump.org/disc/13389/) 90 | * [MediEvil Germany](http://redump.org/disc/25542/) 91 | * [MediEvil Italy](http://redump.org/disc/29475/) 92 | * [MediEvil Spain](http://redump.org/disc/1584/) 93 | 94 | APrip bypasses For LibCrypt v1: [Icepick Bypass](#icepick-bypass). 95 | 96 | ![MediEvil Europe LibCrypt v1 Screen Of Death](images/medievil-europe-lc1-screen.png.png) 97 | 98 | ### LibCrypt v2 99 | 100 | This is a serious upgrade to LibCrypt v1. Not only does it check for evidence that the running game is a burned backup copy, it checks for non-stealth mod-chips and the presence of a Pro Action Replay cheat device installed to the console. This version of LibCrypt is the most common version of the protection. 101 | 102 | APrip bypasses For LibCrypt v2: [B.A.D Bypass](#bad-bypass). 103 | 104 | ## Soft-Mods 105 | 106 | These PSX soft-mods are capable of correctly playing APrip [patched BIN+CUE CD images](#patching-the-cd-image): 107 | 108 | * [Stealth Unlocker](https://www.psxdev.net/forum/viewtopic.php?t=3966) 109 | 110 | * [Tonyhax International](https://alex-free.github.io/tonyhax-international) 111 | 112 | * [Tonyhax](https://orca.pet/tonyhax/) 113 | 114 | * [UniROM](https://unirom.github.io/) (NOT SCPH-1000 or early SCPH-3000). 115 | 116 | Out of the above, all except UniROM have anti-piracy bypass systems implemented for at least some games (and dare I say Tonyhax International's supported bypasses are [quite extensive](https://alex-free.github.io/tonyhax-international/anti-piracy-bypass.html#apv2-bypasses)). 117 | 118 | For any games which don't have a bypass implemented, APrip is here to save the day. [Tonyhax International](https://alex-free.github.io/tonyhax-international) even has a [GameShark feature](https://alex-free.github.io/tonyhax-international/gameshark-code-support.html) that allows you to use [APrip generated GameShark codes](#generating-gameshark-codes). 119 | 120 | There are no guarantees that aprip bypasses will work on something like the PSX Change v2, Swap Magic Disc, or similar due to those not updating TOC data before booting the discs. 121 | 122 | Besides the Fake VC0 Bypass, all other bypasses do support non-stealth mod-chip consoles. 123 | 124 | ## Patching The CD Image 125 | 126 | APrip can patch the CD image BIN file, as long as the protection is not obfuscated by compression or encryption. If you are unable to patch the CD image you should try generating [Gameshark codes](#generating-gameshark-codes), which work around any such obfuscation entirely. 127 | 128 | CD image is slow as it has to scan the entire file for the various protections. Depending on how fast the computer you run APrip on is (and how big the BIN file is), this may take a literal minute or 2 to complete. Maybe more for slower systems. 129 | 130 | The first thing to do is to rip and or acquire a CD image of your desired game using any CD ripping software of your choice. Then depending on what protection the game requires, follow the instructions below for either APv2 or LibCrypt v1/LibCrypt v2. 131 | 132 | ### For APv1 or APv2, Or No-Swap 133 | 134 | * On Linux, execute `./aprip -b ` in your Terminal. On Windows, execute `aprip.exe -b ` in `cmd.exe`/Windows Terminal. 135 | 136 | Replace `` with the actual first or only track of the cd image ripped from the game you want to patch_. 137 | 138 | Make sure you get at least one match for the [Append No Swap Bypass](#append-no-swap-bypass), the [Zero Bypass](#zero-bypass), the [Fake VC0 Bypass](#fake-vc0), or the [Fake PAL Bypass](#fake-pal-bypass). If you didn't get any match then aprip can't patch the disc image of the game. It might be able to generate [GameShark codes](#generating-gameshark-codes) however. 139 | 140 | ![Ape Escape Europe Disc Image Patched](images/ape-escape-europe-disc-image-patched.png) 141 | 142 | ![Dino Crisis 2 Japan BIN patched](images/dino-crisis-2-japan-disc-image-patched.png) 143 | 144 | ### For LibCrypt v1/LibCrypt v2 145 | 146 | Make sure you Enable the `Allow Booting Without SBI File` if your going to be testing your patched disc image in DuckStation. 147 | 148 | ![Duckstation changed settings](images/duckstation-changed-settings.png) 149 | 150 | On Linux, execute `./aprip -b ` in your Terminal. On Windows, execute `aprip.exe -b ` in `cmd.exe`/Windows Terminal. 151 | 152 | Replace `` with the actual first or only track of the cd image ripped from the game you want to patch. 153 | 154 | Replace `` with the correct magic word (MW:), which you can find from the [PS1 Custom Patches](https://www.psdevwiki.com/ps3/PS1_Custom_Patches) page on the PS3 Dev Wiki among other places. 155 | 156 | Make sure you get a match for the [ICEPICK Bypass](#icepick-bypass) or the [B.A.D Bypass](#bad-bypass). If you didn't get any match then aprip can't patch the disc image of the game. It might be able to generate [GameShark codes](#generating-gameshark-codes) however. 157 | 158 | ![Parasite Eve LibCrypt v2 Patched](images/parasite-eve-2-europe-disc-1-cd-image-patched.png) 159 | 160 | ![MediEvil France LibCrypt v1 patched](images/medievil-france-disc-image-patched.png) 161 | 162 | ## Generating GameShark Codes 163 | 164 | Instead of patching the BIN file of the CD image directly (or if you have a real import PSX disc), you can generate GameShark codes to do the same thing. GameShark code generation more reliably finds bypasses due to some CD images obfuscating the protection by compression or encryption, which isn't a problem at run time. First, You'll need the download the [DuckStation](https://github.com/stenzek/duckstation/releases) emulator and change some of the default options. 165 | 166 | ### DuckStation Configuration 167 | 168 | Start the emulator and navigate to the `Advanced` Settings pane. 169 | 170 | ![Duckstation open settings](images/duckstation-open-settings.png) 171 | 172 | ![Duckstation default settings](images/duckstation-default-settings.png) 173 | 174 | Change your settings pane so it looks like this image below: 175 | 176 | 1) Log Level to `Developer`. 177 | 2) Enable `Log To System Console`. 178 | 3) Enable `Log To Debug Console`. 179 | 4) Enable `Log To File`. 180 | 5) Scroll down in the `Tweaks/Hacks` section and enable `Allow Booting Without SBI File`. 181 | 6) Enable `Show Debug Menu` (if not already enabled). 182 | 183 | ![Duckstation changed settings](images/duckstation-changed-settings.png) 184 | 185 | Also, ensure that the _Enable 8MB RAM_ option is **left untouched** (which is the default) as shown in the image below: 186 | 187 | ![Duckstation RAM settings](images/duckstation-ram-settings.png) 188 | 189 | For APv1/APv2 games, make sure you have a Japanese or USA BIOS selected. For LibCrypt make sure you have a European BIOS selected. I also recommend to enable the `Fast Boot` option. 190 | 191 | ![DuckStation BIOS Settings](images/duckstation-bios-settings.png) 192 | 193 | Last, make sure you disable the Save State On Shutdown option, to make sure you don't later interfere with testing your bypasses: 194 | 195 | ![DuckStation Save State Setting](images/duckstation-save-state-setting.png) 196 | 197 | ### Dumping The RAM 198 | 199 | For Linux, execute `./ap-type-checker.sh ` in your Terminal. For Windows, execute `ap-type-checker.bat ` in your `cmd.exe`/Windows Terminal. 200 | 201 | Replace `` with the actual file path of the DuckStation executable. 202 | 203 | Replace `` with the target game's cue file that contains additional anti-piracy copy protection. 204 | 205 | ![Dino Crisis USA Rev 0 AP Type Checker](images/ap-type-checker-dino-crisis-usa-rev-0.png) 206 | 207 | ![Dino Crisis USA Rev 0 AP Type Checker](images/ap-type-checker-resident-evil-3-germany.png) 208 | 209 | This will launch DuckStation with your game. If you are using a LibCrypt title, you'll need to confirm that you do want to boot the game without the SBI file: 210 | 211 | ![DuckStation Confirm No SBI](images/duckstation-confirm-no-sbi.png) 212 | 213 | Now with the game running, you need to figure out at what point that the protection is executed, and then you need to pause the emulation (use the space key). For LibCrypt v1, this is during the loading of the Hilltop Mausoleum level. For LibCrypt v2, APv1, and APv1 this various widely. Most APv2 and APv1 games however run the protection shortly after execution. The good news is (for APv1/APv2) you can see this protection being ran by monitoring the terminal, when you see the yellow text `as it is a data sector and we're not reading` immediately pause emulation and dump the memory to a file as the anti-piracy code has begun running at that time. For LibCrypt v2, you'll need to figure out the effects that the protection triggers (and when they start), which are game specific. This is usually evident by a ton of `CDROM GetlocP` commands being sent in succession. The key part is to dump the RAM as soon as the protection begins to execute. 214 | 215 | ![Dino Crisis USA Rev 0 AP Type Checker Dump RAM](images/dino-crisis-usa-rev-0-ram-dump.png) 216 | 217 | ![Resident Evil 3 Germany AP Type Checked Dump RAM](images/resident-evil-3-germany-dump-ram.png) 218 | 219 | ### Using The RAM Dump 220 | 221 | For APv1/APv2 on Linux, execute `./aprip -gs ` in your Terminal. For APv1/APv2 On Windows, execute `aprip.exe -gs ` in `cmd.exe`/Windows Terminal. 222 | 223 | For LibCrypt v1/LibCrypt v2 on Linux, execute `./aprip -gs ` in your Terminal. For LibCrypt v1/LibCrypt v2 On Windows, execute `aprip.exe -gs ` in `cmd.exe`/Windows Terminal. 224 | 225 | Replace `` with the correct magic word (MW:), which you can find from the [PS1 Custom Patches](https://www.psdevwiki.com/ps3/PS1_Custom_Patches) page on the PS3 Dev Wiki among other places. 228 | 229 | ![Dino Crisis USA Rev 0 Code Generation](images/dino-crisis-usa-rev-0-code-generation.png) 230 | 231 | ![Resident Evil 3 Germany Code Generation](images/resident-evil-3-germany-code-generation.png) 232 | 233 | DuckStation has built in cheat code support that you can use to test your generated bypass. [Tonyhax International](https://alex-free.github.io/tonyhax-international) also has a [GameShark feature](https://alex-free.github.io/tonyhax-international/gameshark-code-support.html) you can use for real hardware. 234 | 235 | ## Converting An Existing GameShark Code 236 | 237 | [Back in the day](https://consolecopyworld.com/psx/psx_game_codes.shtml) GameShark codes to bypass additional anti-piracy copy protection were usually only made for a single revision/version of a game. Using APrip, you can most likely port that the GameShark code code to a different version of a game (think a Demo version, Greatest Hits version, or different regional release). Sometimes even a GameShark code for the sequel or prequel of the game with an existing code can be ported. 238 | 239 | The example usage below will explain how the existing Biohazard 3: The Last Escape [Japan Rev 0](http://redump.org/disc/10/) version [GameShark code](https://consolecopyworld.com/psx/psx_game_codes_b.shtml) can be converted/ported by APrip for the Biohazard 3: The Last Escape [Japan Demo](http://redump.org/disc/11674/) version. 240 | 241 | * On Linux, execute `./ap-type-checker.sh ` in your Terminal. On Windows, execute `ap-type-checker.bat ` in `cmd.exe` or the Windows terminal. 242 | 243 | Replace `` with the actual file path of the DuckStation executable. 244 | 245 | Replace `` with the game the existing GameShark code is for. 246 | 247 | ![AP Type Checker Resident Evil 3 Japan](images/ap-type-checker-resident-evil-3-japan.png) 248 | 249 | Monitor the terminal, when you see the yellow text `as it is a data sector and we're not reading` immediately pause emulation and dump the memory to a file, as is done in [GameShark Code Generation](#generating-gameshark-codes). 250 | 251 | ![Resident Evil 3 Japan Rev 0 Dump RAM](images/resident-evil-3-japan-rev-0-dump-ram.png) 252 | 253 | Once you have dumped the RAM for the version of the game you have a code for, repeat the exact same process for the version of the game you are converting/porting the GameShark code to. 254 | 255 | ![AP Type Checker Resident Evil 3 Japan Demo](images/ap-type-checker-resident-evil-3-japan-demo.png) 256 | 257 | ![Resident Evil 3 Japan Demo Dump RAM](images/resident-evil-3-japan-demo-dump-ram.png) 258 | 259 | In this example, the GameShark code we know for Biohazard 3 Japan Rev 0 is: [D01840E2 1040 801840E2 1000](https://consolecopyworld.com/psx/psx_game_codes_b.shtml). The first line of the code is the one that is to be input to APrip, as the APrip conversion feature requires a D0XXXXXX YYYY type code. 260 | 261 | On Linux for this example, execute `./aprip D01840E2 1040 14` in your Terminal. 262 | 263 | On Windows, execute `aprip.exe D01840E2 1040 14`. 264 | 265 | Replace `` with the memory dump file from the version of the game you know the GameShark code for. 266 | 267 | Replace `` with the memory dump of the version of the game you are converting/porting the existing GameShark code to. 268 | 269 | The `14` is the verbosity of the pattern match. In simplest terms, if you are not getting any GameShark code generated lower this number and try again until you do (or don't, if the protection code is changed enough between games). If you are getting multiple GameShark codes being generated for the conversion, raise this number until you get only one match. If you can't get only one match, you'll need to do some trial and error testing to see which GameShark code actually does what you want for the target game. 270 | 271 | ![Resident Evil 3 Japan To Japan Demo Conversion](images/resident-evil-3-japan-to-japan-demo-conversion.png) 272 | 273 | So in this example, we took the Resident Evil 3 Japan code: 274 | 275 | `D01840E2 1040` 276 | `801840E2 1000` 277 | 278 | and were able to get a working code for Resident Evil 3 Japan Demo: 279 | 280 | `D01800E2 1040` 281 | `801800E2 1000` 282 | 283 | The following `80XXXXXX` `YYYY` line should be obvious to figure out from the original code. 284 | 285 | ## Comparing CD Images 286 | 287 | You can find out how a patch works by comparing the differences between an unmodified disc image and the patched one. APrip makes this very easy with the sector user data compare mode. This mode only outputs bytes that differ found in the user data portion of sectors. By design it ignores EDC and ECC because these are already known to change if any bytes of the user data are different, and are _usually_ just noise in deciphering what a patch is actually doing. 288 | 289 | To use this feature, use aprip with 3 arguments. `-d `. Replace `` with the original unmodified track 1 bin file of the game. Replace `` with the patched track 1 bin file of the same game. You then get a very clear overview of what the patch is doing. 290 | 291 | ## Creating APrip-Style Patches 292 | 293 | Building off of the above features, you can generate an aprip-style patch. An APrip-style patch searches all sector user data for a pattern of bytes (limit of 2 sector's user data (MAX is 0x1000 byte pattern/4096 lines) and then patches specific bytes in the pattern (pre-defined in the patch) without using known offsets. It can be used to 'port' patch methods. 294 | 295 | To use this feature, first use aprip with 4 arguments. `-d `. Replace `` with the original unmodified track 1 bin file of the game. Replace `bin file 2` with the patched track 1 bin file of the same game. Replace `` with the name of the patch file to be generated. 296 | 297 | Then use aprip with 3 arguments. `-p `. Replace `` with the unmodified disc image. Replace `` with the patch file previously generated. 298 | 299 | If your trying to port a LibCrypt patch, be aware that the magic word needs to be changed in the patch file for whatever other game your attempting to port the patch to. 300 | 301 | ## APrip Bypasses 302 | 303 | ### Zero Bypass 304 | 305 | If you see `Got APv1 Zero bypass (By Alex Free) match` then the game should work now work even with a non-stealth mod-chip. The Zero Bypass only works for the earliest APv1 games however. 306 | 307 | ### Fake VC0 308 | 309 | If you see `Got APv2 Fake VC0 bypass (By Alex Free) match` then the game for sure 100% works if your console doesn't have a non-stealth mod-chip and your using a soft-mod. The Fake VC0 bypass is found in all APv2 games, but it doesn't bypass enough of the protection to make non-stealth mod-chip consoles work. 310 | 311 | ### Fake PAL Bypass 312 | 313 | If you see `Got APv2 Fake PAL bypass (By Alex Free & MottZilla) match` then the game will now most likely work on both non-stealth mod-chip and unmodified consoles. Only a few games don't have this bypass, or have the bypass but it doesn't work. 314 | 315 | ### ICEPICK Bypass 316 | 317 | If you see `Got LibCrypt v1 bypass match (By ICEPICK)` then you have some PAL release of MediEvi. This bypass allows the game to be burned with any standard CD burning software using just the cue file. This bypass has 100% compatibility with all game discs containing LibCrypt v1. 318 | 319 | ### B.A.D Bypass 320 | 321 | If you see `Got LibCrypt v2 bypass match (By B.A.D)` then you have some a PAL game containing LibCrypt v2 protection, which is the most common version of LibCrypt. This bypass allows the game to be burned with any standard CD burning software using just the cue file. It also allows for use with non-stealth mod-chip consoles. This bypass should have quite high compatibility if the game is LibCrypt v2. 322 | 323 | ### Append No Swap Bypass 324 | 325 | If you see `Append No Swap Bypass match (By mdmdj)` then you have either [Beat Mania Append GottaMix](http://redump.org/disc/3550/) or [Beat Mania Append 3rdMix](http://redump.org/disc/2306/). Under normal circumstances, you must first use a Beat Mania [Key Disc](https://remywiki.com/KEY_DISC) to boot these [Append Discs](https://remywiki.com/APPEND_DISC). After this patch, the games will work standalone when booted directly, instead of displaying a message to insert a key disc first. 326 | 327 | ## How It Works 328 | 329 | ### APv1 Zero Bypass 330 | 331 | I found that zeroing out the `19'04` and `19'05` CDROM test commands meant that for the earliest APv1 games (PoPoRoGue Japan, Ape Escape Japan, Final Fantasy VIII, Pocket MuuMuu) they were never sent. The protection doesn't even check if they were sent and responded as expected for these early games, allowing this to work. 332 | 333 | ### APv2 Fake VC0 Bypass 334 | 335 | I noticed in the partial [decompilation](https://github.com/socram8888/tonyhax/blob/master/docs/apv2.c) of an APv2 style function that the CD commands used could be identifiable from memory when the checks are being performed. This turned out to be true for seemingly every release APv2 game. 336 | 337 | In the APv2 code, the ReadTOC command is the one that prevents [soft-mods](#soft-mods) from playing games on unmodified consoles. ReadTOC was added in the VC1A CDROM controller firmware, and is present in all but the SCPH-1000 and early SCPH-3000 consoles (which use VC0A or VC0B CDROM controller firmware). ReadTOC is really interesting because it resets the CD drive authentication/licensed disc status, which before it's introduction could only be done when the CD drive door is physically opened. ReadTOC was actually added to the boot sequence of all consoles starting in late 1995 (which have at least BIOS v2.1 and CDROM firmware VC1B) to patch the [CD Player Swap Trick](https://alex-free.github.io/tonyhax-international/#boot-cd) and to make other [traditional swap trick](https://gamefaqs.gamespot.com/ps/916392-playstation/faqs/4708) either impossible or much more involved to pull off. In later PSX games ReadTOC is ran in game-code to detect unlicensed discs after game boot for additional verification. 338 | 339 | APv2 protected games do accommodate for the SCPH-1000 and early SCPH-3000 consoles which lack the ReadTOC command due to the CDROM controller firmware versions in these consoles being shipped before it's introduction. From my understanding, this semi-discreet line of decompiled [code](https://github.com/alex-free/tonyhax/blob/d8f5c5fe4171ecb24a0522bac60b879a2deca4d3/docs/antipiracy.c#L109) is what triggers if the ReadTOC command does not exist. What happens is essentially a fail-safe, the code immediately returns 0 and exits out of the function. **Now this is really interesting**, because this means that if the TOC is correct (which [soft-mods](#soft-mods) guarantee), then the AP function will always pass, no patching required. And it's true, you can literally any PSX game with APv2 protection and it will **just work** on these early consoles. So this is where I tried something. What happens if we replace the first byte of the ReadTOC command with `00` and leave the rest of the bytes relative to it in the AP code alone? 00 would turn ReadToc into effectively the [Sync](https://problemkaputt.de/psx-spx.htm#cdromcontrollercommandsummary) command, which is actually an invalid command that doesn't do what it's supposed to do according to official documentation. Effectively it turns out this seems to trigger the same behavior as if the console was a SCPH-1000 or early SCPH-3000 that lacked the ReadTOC command, bypassing the APv2 code completely for all unmodified console models. 340 | 341 | ### APv2 Fake PAL Bypass 342 | 343 | Many games that got a PAL region release contain code in even the USA/Japanese releases which when detecting a PAL BIOS they disable the protection completely. This can be easily faked to always detect a PAL BIOS and therefore never run the protection on any console. 344 | 345 | ### LibCrypt v1 ICEPICK Bypass 346 | 347 | I found the [TRSIMEDI patcher by ICEPICK](https://consolecopyworld.com/psx/psx_medievil.shtml) and reverse engineered how it works. 348 | 349 | ### LibCrypt v2 B.A.D Bypass 350 | 351 | I was able to automate the hex editing guide written at the [LibCrypt PS1 Protection Bible By Red-J](https://red-j.github.io/Libcrypt-PS1-Protection-bible/index.htm). 352 | 353 | ### Append No Swap Bypass 354 | 355 | [Mdmdj](https://github.com/alex-free/aprip/issues/1) pointed out that for the Beat Mania 3 engine-based [Append Discs](https://remywiki.com/APPEND_DISC) [Beat Mania Append GottaMix](http://redump.org/disc/3550/) and [Beat Mania Append 3rdMix](http://redump.org/disc/2306/) you can edit `SYSTEM.CNF` to boot the `APPEND.EXE` file directly in order to remove the limitation. 356 | 357 | ## License 358 | 359 | APrip is released as open source software under the 3-BSD license. Please see the file `license.txt` in each release for more info. 360 | 361 | ## Credits 362 | 363 | * [MottZilla](https://github.com/mottzilla) for guidance in developing the CD patcher methods. Also co-developed the [Fake PAL Bypass](#fake-pal-bypass), after [FlareRez](https://gbatemp.net/threads/aprip-patch-out-additional-anti-piracy-copy-protection-found-in-some-later-psx-games.621366/page-2#post-10244014) pointed out the PAL console behavior to me. 364 | 365 | * [Socram8888](https://github.com/socram8888) did the the partial [decompilation](https://github.com/socram8888/tonyhax/blob/master/docs/apv2.c) of the APv2 anti-piracy function, found in the docs folder of the original [Tonyhax](https://github.com/socram8888/). This enabled me to develope aprip's APv2 bypass for unmodified consoles. 366 | 367 | * [Red-J](https://github.com/red-j) for the [LibCrypt PS1 Protection Bible](https://red-j.github.io/Libcrypt-PS1-Protection-bible/index.htm), which helped me add support for LibCrypt v1 and LibCrypt v2. -------------------------------------------------------------------------------- /variables.mk: -------------------------------------------------------------------------------- 1 | # Variables for EzRe Makefile 2 | 3 | # Basename of all release files (.zip, .deb) 4 | RELEASE_BASE_NAME=aprip 5 | # Version number, passed as 'VERSION' string to $(SOURCE_FILES) 6 | VERSION=v1.0.9 7 | # Appeneded to end of release file name 8 | LINUX_I386_RELEASE_NAME_SUFFIX=linux-i386-static 9 | LINUX_X86_64_RELEASE_NAME_SUFFIX=linux-x86_64-static 10 | WINDOWS_I686_RELEASE_NAME_SUFFIX=windows-i686-static 11 | WINDOWS_X86_64_RELEASE_NAME_SUFFIX=windows-x86_64-static 12 | # Release file format is $(RELEASE_BASE_NAME)-$(VERSION)-$(RELEASE_NAME_SUFFIX) 13 | 14 | # Files included in all portable releases (.zip) 15 | RELEASE_FILES=images readme.md changelog.md license.md 16 | # OPTIONAL: files included only in the Linux portable releases (.zip) 17 | LINUX_SPECIFIC_RELEASE_FILES=cd-command-logger.sh ap-type-checker.sh 18 | # OPTIONAL: files included only in the Windows portable releases (.zip) 19 | WINDOWS_SPECIFIC_RELEASE_FILES=cd-command-logger.bat ap-type-checker.bat 20 | 21 | # All dependencies required to build the software, to be installed when using deps-apt EzRe Makefile rule (For Debian/Ubuntu) 22 | BUILD_DEPENDS_APT=build-essential gcc-multilib mingw-w64-tools zip dpkg-dev 23 | 24 | # Executable name in release (.exe file extension is appended for Windows builds) 25 | PROGRAM=aprip 26 | # Source files to be compiled into $(PROGRAM) target 27 | SOURCE_FILES=*.c 28 | # Compiler flags used to compile $(SOURCE_FILES) 29 | COMPILER_FLAGS=-Wall -Werror -Ofast -static 30 | # Compiler flag appended to $(COMPILER_FLAGS) to compile $(SOURCE_FILES) for Linux x86 builds 31 | COMPILER_FLAGS_LINUX_X86=-m32 32 | # Create builds in this directory relative to $(SOURCE_FILES) 33 | BUILD_DIR=build 34 | 35 | # Linux Compiler For i386 and x86_64 36 | LINUX_COMPILER=gcc 37 | # Windows Cross Compiler For i686 38 | WINDOWS_X86_COMPILER=i686-w64-mingw32-gcc 39 | # Windows Cross Compiler For x86_64 40 | WINDOWS_X86_64_COMPILER=x86_64-w64-mingw32-gcc 41 | # Host system compiler 42 | COMPILER=$(LINUX_COMPILER) 43 | --------------------------------------------------------------------------------