diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 33dec3a..134704f 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -2,9 +2,9 @@ name: CMake on: push: - branches: [ "main", "develop" ] + branches: '*' pull_request: - branches: [ "main", "develop" ] + branches: 'v*' env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) @@ -62,15 +62,22 @@ jobs: - name: Rename Debug USB uf2 run: mv build_usb/sd2psx.uf2 build/sd2psx_usb_debug.uf2 + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: SD2PSX-${{ github.sha }} + path: | + build/*.uf2 + - uses: marvinpinto/action-automatic-releases@v1.2.1 - if: github.event_name != 'pull_request' + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') #only release on tag or master branch # uses: marvinpinto/action-automatic-releases@919008cf3f741b179569b7a6fb4d8860689ab7f0 with: # GitHub secret token title: "${{ env.SD2PSX_VERSION }}" repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: ${{ env.SD2PSX_RLS_TAG }} - prerelease: true + prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') }} # Assets to upload to the release files: | build/*.uf2 diff --git a/CMakeLists.txt b/CMakeLists.txt index f1a23ae..de3873f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,145 +1,107 @@ cmake_minimum_required(VERSION 3.12) - set(PICO_SDK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/ext/pico-sdk) include(pico_sdk_import.cmake) -project(pico_examples C CXX ASM) +project(SD2PSX LANGUAGES C CXX ASM) + set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # set(PICO_COPY_TO_RAM 1) pico_sdk_init() -set(LV_CONF_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/lv_conf.h CACHE STRING "" FORCE) -add_subdirectory(ext/lvgl EXCLUDE_FROM_ALL) +# Add all subdirectories for sub-targets add_subdirectory(database) add_subdirectory(ps2boot) add_subdirectory(src/version) +add_subdirectory(src/ps2) +add_subdirectory(src/ps1) +add_subdirectory(ext/) + +# SD2PSX Main Lib add_executable(sd2psx src/main.c - src/debug.c - src/gui.c - src/input.c - src/ui_menu.c - src/ui_theme_mono.c - src/des.c - src/keystore.c - src/settings.c - src/bigmem.c - src/oled.c - - src/ps1/ps1_cardman.c - src/ps1/ps1_dirty.c - src/ps1/ps1_memory_card.c - src/ps1/ps1_empty_card.c - src/ps1/ps1_odeman.c - - src/ps2/ps2_memory_card.c - src/ps2/ps2_dirty.c - src/ps2/ps2_cardman.c - src/ps2/ps2_pio_qspi.c - src/ps2/ps2_psram.c - src/ps2/ps2_exploit.c + src/game_names/game_names.c src/wear_leveling/wear_leveling.c src/wear_leveling/wear_leveling_rp2040_flash.c - src/arduino_wrapper/sd.cpp - src/arduino_wrapper/SPI.cpp - - ext/ESP8266SdFat/src/common/FmtNumber.cpp - ext/ESP8266SdFat/src/common/FsCache.cpp - ext/ESP8266SdFat/src/common/FsDateTime.cpp - ext/ESP8266SdFat/src/common/FsName.cpp - ext/ESP8266SdFat/src/common/FsStructs.cpp - ext/ESP8266SdFat/src/common/FsUtf.cpp - ext/ESP8266SdFat/src/common/upcase.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatDbg.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatFile.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatFilePrint.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatFileWrite.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatFormatter.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatName.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatPartition.cpp - ext/ESP8266SdFat/src/ExFatLib/ExFatVolume.cpp - ext/ESP8266SdFat/src/FatLib/FatDbg.cpp - ext/ESP8266SdFat/src/FatLib/FatFile.cpp - ext/ESP8266SdFat/src/FatLib/FatFileLFN.cpp - ext/ESP8266SdFat/src/FatLib/FatFilePrint.cpp - ext/ESP8266SdFat/src/FatLib/FatFileSFN.cpp - ext/ESP8266SdFat/src/FatLib/FatFormatter.cpp - ext/ESP8266SdFat/src/FatLib/FatName.cpp - ext/ESP8266SdFat/src/FatLib/FatPartition.cpp - ext/ESP8266SdFat/src/FatLib/FatVolume.cpp - ext/ESP8266SdFat/src/FreeStack.cpp - ext/ESP8266SdFat/src/FsLib/FsFile.cpp - ext/ESP8266SdFat/src/FsLib/FsNew.cpp - ext/ESP8266SdFat/src/FsLib/FsVolume.cpp - ext/ESP8266SdFat/src/iostream/istream.cpp - ext/ESP8266SdFat/src/iostream/ostream.cpp - ext/ESP8266SdFat/src/iostream/StdioStream.cpp - ext/ESP8266SdFat/src/iostream/StreamBaseClass.cpp - ext/ESP8266SdFat/src/MinimumSerial.cpp - ext/ESP8266SdFat/src/SdCard/SdCardInfo.cpp - ext/ESP8266SdFat/src/SdCard/SdSpiCard.cpp - ext/fnv/hash_64a.c ) -add_library(ssd1306 STATIC ext/pico-ssd1306/ssd1306.c) -target_link_libraries(ssd1306 pico_stdlib hardware_i2c) - target_compile_definitions( sd2psx PUBLIC PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 PICO_FLASH_SIZE_BYTES=16777216 - USE_SPI_ARRAY_TRANSFER=1 -) - -target_compile_options( - sd2psx PRIVATE - -Wall -Wextra - -fno-jump-tables ) target_include_directories(sd2psx PUBLIC - src/arduino_wrapper - - ext/pico-ssd1306 - ext/ESP8266SdFat/src - ext/ESP8266SdFat/extras/attic ext/fnv ) -pico_generate_pio_header(sd2psx ${CMAKE_CURRENT_LIST_DIR}/src/ps1/ps1_mc_spi.pio) -pico_generate_pio_header(sd2psx ${CMAKE_CURRENT_LIST_DIR}/src/ps2/ps2_mc_spi.pio) -pico_generate_pio_header(sd2psx ${CMAKE_CURRENT_LIST_DIR}/src/ps2/ps2_qspi.pio) - target_link_libraries(sd2psx - ssd1306 - pico_stdlib - pico_multicore - hardware_pio - hardware_spi - hardware_i2c - hardware_flash - hardware_dma - lvgl::lvgl - gamedb - ps2boot - sd2psx_version + PRIVATE + pico_stdlib + pico_multicore + hardware_pio + hardware_i2c + hardware_flash + gamedb + ps2boot + sd2psx_version + sd2psx_common + ps1_card + ps2_card + sd_fat ) add_dependencies(sd2psx gamedb ps2boot) set_target_properties(sd2psx PROPERTIES PICO_TARGET_LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/memmap_custom.ld) +# Common Lib + +add_library(sd2psx_common STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/src/debug.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/gui.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/input.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ui_menu.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ui_theme_mono.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/des.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/keystore.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/settings.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/bigmem.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/oled.c) + +target_include_directories(sd2psx_common + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src + PRIVATE ) + +target_link_libraries(sd2psx_common + PUBLIC + lvgl::lvgl + PRIVATE + pico_platform_headers + ssd1306 + hardware_flash) + +target_compile_options(sd2psx_common + PUBLIC + -Wall -Wextra + -fno-jump-tables) + +target_compile_definitions(sd2psx_common PUBLIC + USE_SPI_ARRAY_TRANSFER=1) + +set_target_properties(sd2psx_common PROPERTIES PICO_TARGET_LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/memmap_custom.ld) + pico_add_extra_outputs(sd2psx) set(DEBUG_USB_UART OFF CACHE BOOL "Activate UART over USB for debugging") if(DEBUG_USB_UART) - add_definitions(-DDEBUG_USB_UART) - pico_enable_stdio_usb(sd2psx ENABLED) + target_compile_definitions(sd2psx_common PUBLIC -DDEBUG_USB_UART) + pico_enable_stdio_usb(sd2psx ON) endif() diff --git a/database/CMakeLists.txt b/database/CMakeLists.txt index f8c5cdf..b31d666 100644 --- a/database/CMakeLists.txt +++ b/database/CMakeLists.txt @@ -1,6 +1,7 @@ +# PS1 set(GAMEDB_PS1_OBJ "${CMAKE_CURRENT_BINARY_DIR}/gamedbps1.o") -add_custom_target(gamedbobjs ALL +add_custom_target(gamedbobjs_ps1 ALL COMMAND ${CMAKE_COMMAND} -D OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR} -D PYTHON_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/parse_db.py @@ -11,6 +12,24 @@ add_custom_target(gamedbobjs ALL BYPRODUCTS ${GAMEDB_PS1_OBJ}) add_library(gamedb INTERFACE) -add_dependencies(gamedb gamedbobjs) +add_dependencies(gamedb gamedbobjs_ps1) target_link_libraries(gamedb INTERFACE ${GAMEDB_PS1_OBJ}) + +# PS2 + +set(GAMEDB_PS2_OBJ "${CMAKE_CURRENT_BINARY_DIR}/gamedbps2.o") + +add_custom_target(gamedbobjs_ps2 ALL + COMMAND ${CMAKE_COMMAND} + -D OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR} + -D PYTHON_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/get_and_parse_hdldb.py + -D CMAKE_OBJCOPY=${CMAKE_OBJCOPY} + -D SYSTEM=ps2 + -P ${CMAKE_CURRENT_SOURCE_DIR}/db_obj_builder.cmake + VERBATIM + BYPRODUCTS ${GAMEDB_PS1_OBJ}) + +add_dependencies(gamedb gamedbobjs_ps2) + +target_link_libraries(gamedb INTERFACE ${GAMEDB_PS2_OBJ}) diff --git a/database/db_obj_builder.cmake b/database/db_obj_builder.cmake index 7f3194d..773eddc 100644 --- a/database/db_obj_builder.cmake +++ b/database/db_obj_builder.cmake @@ -2,7 +2,18 @@ string(TIMESTAMP date "%Y%m%d") if(NOT EXISTS "${OUTPUT_DIR}/gamedb${SYSTEM}_${date}.o") -find_package (Python COMPONENTS Interpreter) +find_package (Python3 COMPONENTS Interpreter) +execute_process (COMMAND "${Python3_EXECUTABLE}" -m venv "${OUTPUT_DIR}/db_builder") + +# Here is the trick +## update the environment with VIRTUAL_ENV variable (mimic the activate script) +set (ENV{VIRTUAL_ENV} "${OUTPUT_DIR}/db_builder") +## change the context of the search +set (Python3_FIND_VIRTUALENV FIRST) +## unset Python3_EXECUTABLE because it is also an input variable (see documentation, Artifacts Specification section) +unset (Python3_EXECUTABLE) +## Launch a new search +find_package (Python3 COMPONENTS Interpreter Development) file(GLOB files "${OUTPUT_DIR}/gamedb${SYSTEM}_*") foreach(file ${files}) @@ -14,7 +25,12 @@ set(GAMEDB_${SYSTEM}_BIN "gamedb${SYSTEM}.dat") set(GAMEDB_${SYSTEM}_OBJ "${OUTPUT_DIR}/gamedb${SYSTEM}_${date}.o") execute_process( - COMMAND ${Python_EXECUTABLE} ${PYTHON_SCRIPT} ${SYSTEM} ${OUTPUT_DIR} + COMMAND ${Python3_EXECUTABLE} -m pip install requests unidecode + WORKING_DIRECTORY ${OUTPUT_DIR} + OUTPUT_QUIET +) +execute_process( + COMMAND ${Python3_EXECUTABLE} ${PYTHON_SCRIPT} ${SYSTEM} ${OUTPUT_DIR} WORKING_DIRECTORY ${OUTPUT_DIR} OUTPUT_QUIET ) diff --git a/database/get_and_parse_hdldb.py b/database/get_and_parse_hdldb.py new file mode 100644 index 0000000..665b552 --- /dev/null +++ b/database/get_and_parse_hdldb.py @@ -0,0 +1,114 @@ +import requests +import csv +from unidecode import unidecode + +class GameId: + name = "" + id = "" + prefix = "" + parent_id = "" + def __init__(self, name, id, parent_id=None): + self.name = unidecode(name) + separator = "_" + if "-" in id: + separator = "-" + self.id = id.split(separator)[1].replace(".", "") + self.prefix = id.split(separator)[0] + if parent_id: + self.parent_id = parent_id.split(separator)[1] + else: + self.parent_id = self.id + def __str__(self): + return "Prefix " + self.prefix + " Id " + self.id + " Name " + self.name + " Parent " + self.parent_id + + def __lt__(self, o): + return self.name < o.name + + +def writeSortedGameList(outfile, prefixes, games_count, games_sorted, gamenames): + term = 0 + # Calculate general offsets + game_ids_offset = (len(prefixes) + 1) * 8 + game_names_base_offset = game_ids_offset + (games_count * 12) + (len(prefixes) * 12) + prefix_offset = game_ids_offset + + offset = game_names_base_offset + game_name_to_offset = {} + # Calculate offset for each game name + for gamename in gamenames: + game_name_to_offset[gamename] = offset + offset = offset + len(gamename) + 1 + # First: write prefix Indices in the format + # 4 Byte: Index Chars, padded with ws in the end + # 4 Byte: Index Offset within dat + for prefix in games_sorted: + adjustedPrefix = prefix + if len(prefix) < 4: + adjustedPrefix = prefix + (4 - len(prefix) ) * " " + outfile.write(adjustedPrefix.encode('ascii')) + outfile.write(prefix_offset.to_bytes(4, 'big')) + prefix_offset = prefix_offset + (len(games_sorted[prefix]) + 1) * 12 + outfile.write(term.to_bytes(8, 'big')) + # Next: write game entries for each index in the format: + # 4 Byte: Game ID without prefix, Big Endian + # 4 Byte: Offset to game name, Big Endian + # 4 Byte: Parent Game ID - if multi disc this is equal to Game ID + for prefix in games_sorted: + for game in games_sorted[prefix]: + #print(game) + outfile.write(int(game.id).to_bytes(4, 'big')) + outfile.write(game_name_to_offset[game.name].to_bytes(4, 'big')) + outfile.write(int(game.parent_id).to_bytes(4, 'big')) + outfile.write(term.to_bytes(12, 'big')) + # Last: write null terminated game names + for game in game_name_to_offset: + outfile.write(game.encode('ascii')) + outfile.write(term.to_bytes(1, 'big')) + +def getGamesHDLBatchInstaller() -> ([], [], {}, int): + prefixes = [] + gamenames = [] + games_sorted = {} + games_count = 0 + + url = "https://github.com/israpps/HDL-Batch-installer/raw/main/Database/gamename.csv" + + r = requests.get(url, allow_redirects=True) + + + if r.status_code == 200: + lines = r.text.split("\n") + csv_reader = csv.reader(lines, delimiter=";") + for row in csv_reader: + if len(row) == 2: + id = row[0] + title = row[1] + game = GameId(row[1], row[0]) + try: + if int(game.id) > 0: + # Create Prefix list and game name list + # Create dict that contains all games sorted by prefix + if game.prefix not in prefixes: + prefixes.append(game.prefix) + if game.name not in gamenames: + gamenames.append(game.name) + if not game.prefix in games_sorted: + games_sorted[game.prefix] = [] + games_sorted[game.prefix].append(game) + games_count += 1 + except ValueError: + print(f"{game} not parsed") + continue + return (prefixes, gamenames, games_sorted, games_count) + + +prefixes = [] +gamenames = [] +games_sorted = {} +games_count = 0 + + +with open("gamedbps2.dat", "wb") as out: + (prefixes, gamenames, games_sorted, games_count) = getGamesHDLBatchInstaller() + writeSortedGameList(out, prefixes, games_count, games_sorted, gamenames) + diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt new file mode 100644 index 0000000..e5a586f --- /dev/null +++ b/ext/CMakeLists.txt @@ -0,0 +1,63 @@ + +add_library(sd_fat STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FmtNumber.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsCache.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsDateTime.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsName.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsStructs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsUtf.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/upcase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatDbg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFilePrint.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFileWrite.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFormatter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatName.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatPartition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatVolume.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatDbg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFileLFN.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFilePrint.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFileSFN.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFormatter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatName.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatPartition.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatVolume.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FreeStack.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FsLib/FsFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FsLib/FsNew.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FsLib/FsVolume.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/istream.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/ostream.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/StdioStream.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/StreamBaseClass.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/MinimumSerial.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/SdCard/SdCardInfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/SdCard/SdSpiCard.cpp + ${PROJECT_SOURCE_DIR}/src/arduino_wrapper/sd.cpp + ${PROJECT_SOURCE_DIR}/src/arduino_wrapper/SPI.cpp +) + +target_include_directories(sd_fat + PRIVATE + ${PROJECT_SOURCE_DIR}/src/ + ${PROJECT_SOURCE_DIR}/src/arduino_wrapper + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src + ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/extras/attic) + +target_link_libraries(sd_fat + PUBLIC + pico_stdlib + hardware_spi) + +target_link_libraries(sd_fat PRIVATE sd2psx_common) + + +add_library(ssd1306 STATIC ${CMAKE_CURRENT_SOURCE_DIR}/pico-ssd1306/ssd1306.c) +target_link_libraries(ssd1306 pico_stdlib hardware_i2c) +target_include_directories(ssd1306 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/pico-ssd1306) + +set(LV_CONF_PATH ${PROJECT_SOURCE_DIR}/src/lv_conf.h CACHE STRING "" FORCE) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lvgl EXCLUDE_FROM_ALL) \ No newline at end of file diff --git a/memmap_custom.ld b/memmap_custom.ld index 8eb3eda..e46168f 100644 --- a/memmap_custom.ld +++ b/memmap_custom.ld @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 7168k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/src/arduino_wrapper/sd.cpp b/src/arduino_wrapper/sd.cpp index 643241d..76400c0 100644 --- a/src/arduino_wrapper/sd.cpp +++ b/src/arduino_wrapper/sd.cpp @@ -108,4 +108,20 @@ extern "C" int sd_exists(const char *path) { extern "C" int sd_filesize(int fd) { return files[fd].fileSize();; +} + +extern "C" int sd_iterate_dir(int dir, int it) { + if (it == -1) { + for (it = 0; it < NUM_FILES; ++it) + if (!files[it].isOpen()) + break; + } + if (!files[it].openNext(&files[dir], O_RDONLY)) { + it = -1; + } + return it; +} + +extern "C" size_t sd_get_name(int fd, char* name, size_t size) { + return files[fd].getName(name, size); } \ No newline at end of file diff --git a/src/game_names/game_names.c b/src/game_names/game_names.c new file mode 100644 index 0000000..a3d82de --- /dev/null +++ b/src/game_names/game_names.c @@ -0,0 +1,283 @@ + +#include "game_names.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "pico/platform.h" + + +#include "../debug.h" +#include "../sd.h" +#include "../settings.h" + +#define MAX_GAME_NAME_LENGTH (127) +#define MAX_PREFIX_LENGTH (5) +#define MAX_GAME_ID_LENGTH (16) +#define MAX_STRING_ID_LENGTH (10) +#define MAX_PATH_LENGTH (64) + +extern const char _binary_gamedbps1_dat_start, _binary_gamedbps1_dat_size; +extern const char _binary_gamedbps2_dat_start, _binary_gamedbps2_dat_size; + +typedef struct { + size_t offset; + uint32_t game_id; + uint32_t parent_id; + const char* name; +} game_lookup; + +bool __time_critical_func(game_names_sanity_check_title_id)(const char* const title_id) { + uint8_t i = 0U; + + char splittable_game_id[MAX_GAME_ID_LENGTH]; + strlcpy(splittable_game_id, title_id, MAX_GAME_ID_LENGTH); + char* prefix = strtok(splittable_game_id, "-"); + char* id = strtok(NULL, "-"); + + while (prefix[i] != 0x00) { + if (!isalpha((int)prefix[i])) { + return false; + } + i++; + } + if (i == 0) { + return false; + } else { + i = 0; + } + + while (prefix[i] != 0x00) { + if (!isdigit((int)id[i])) { + return false; + } + i++; + } + + return (i > 0); +} + +#pragma GCC diagnostic ignored "-Warray-bounds" +static uint32_t game_names_char_array_to_uint32(const char in[4]) { + char inter[4] = {in[3], in[2], in[1], in[0]}; + uint32_t tgt; + memcpy((void*)&tgt, (void*)inter, sizeof(tgt)); + return tgt; +} +#pragma GCC diagnostic pop + +static uint32_t game_names_find_prefix_offset(uint32_t numericPrefix, const char* const db_start) { + uint32_t offset = UINT32_MAX; + + const char* pointer = db_start; + + while (offset == UINT32_MAX) { + uint32_t currentprefix = game_names_char_array_to_uint32(pointer), currentoffset = game_names_char_array_to_uint32(&pointer[4]); + + if (currentprefix == numericPrefix) { + offset = currentoffset; + } + if ((currentprefix == 0U) && (currentoffset == 0U)) { + break; + } + pointer += 8; + } + + return offset; +} + +static game_lookup build_game_lookup(const char* const db_start, const size_t db_size, const size_t offset) { + game_lookup game = {}; + size_t name_offset; + game.game_id = game_names_char_array_to_uint32(&(db_start)[offset]); + game.offset = offset; + game.parent_id = game_names_char_array_to_uint32(&(db_start)[offset + 8]); + name_offset = game_names_char_array_to_uint32(&(db_start)[offset + 4]); + if ((name_offset < db_size) && ((db_start)[name_offset] != 0x00)) + game.name = &((db_start)[name_offset]); + else + game.name = NULL; + + return game; +} + +static bool file_has_extension(const char* const file, const char* const extension) { + int file_index = strlen(file) - 1; + int ext_index = strlen(extension) - 1; + while ((file_index >= 0) && (ext_index >= 0)) + if (file[file_index--] != extension[ext_index--]) + return false; + + return true; +} + +static void file_remove_extension(char* const file) { + int file_index = strlen(file) - 1; + while ((file_index >= 0) && (file[file_index] != '.')) { + file[file_index--] = 0x00; + } + if (file_index >= 0) + file[file_index] = 0x00; +} + +static game_lookup find_game_lookup(const char* game_id) { + char prefixString[MAX_PREFIX_LENGTH] = {}; + char idString[10] = {}; + uint32_t numeric_id = 0, numeric_prefix = 0; + + const char* const db_start = settings_get_mode() == MODE_PS1 ? &_binary_gamedbps1_dat_start : &_binary_gamedbps2_dat_start; + const char* const db_size = settings_get_mode() == MODE_PS1 ? &_binary_gamedbps1_dat_size : &_binary_gamedbps2_dat_size; + + uint32_t prefixOffset = 0; + game_lookup ret = { + .game_id = 0U, + .parent_id = 0U, + .name = NULL + }; + + + if (game_id != NULL && game_id[0]) { + char* copy = strdup(game_id); + char* split = strtok(copy, "-"); + + if (strlen(split) > 0) { + strlcpy(prefixString, split, MAX_PREFIX_LENGTH); + for (uint8_t i = 0; i < MAX_PREFIX_LENGTH - 1; i++) { + prefixString[i] = toupper((unsigned char)prefixString[i]); + } + } + + split = strtok(NULL, "-"); + + if (strlen(split) > 0) { + strlcpy(idString, split, 11); + numeric_id = atoi(idString); + } + } + + numeric_prefix = game_names_char_array_to_uint32(prefixString); + + if (numeric_id != 0) { + + prefixOffset = game_names_find_prefix_offset(numeric_prefix, db_start); + + if (prefixOffset < (size_t)db_size) { + uint32_t offset = prefixOffset; + game_lookup game; + do { + game = build_game_lookup(db_start, (size_t)db_size, offset); + + if (game.game_id == numeric_id) { + ret = game; + debug_printf("Found ID - Name Offset: %d, Parent ID: %d\n", (int)game.name, game.parent_id); + debug_printf("Name:%s\n", game.name); + + } + offset += 12; + } while ((game.game_id != 0) && (offset < (size_t)db_size) && (ret.game_id == 0)); + } + } + + return ret; +} + +void __time_critical_func(game_names_extract_title_id)(const uint8_t* const in_title_id, char* const out_title_id, const size_t in_title_id_length, const size_t out_buffer_size) { + uint16_t idx_in_title = 0, idx_out_title = 0; + + while ( (in_title_id[idx_in_title] != 0x00) + && (idx_in_title < in_title_id_length) + && (idx_out_title < out_buffer_size) ) { + if ((in_title_id[idx_in_title] == ';') || (in_title_id[idx_in_title] == 0x00)) { + out_title_id[idx_out_title++] = 0x00; + break; + } else if ((in_title_id[idx_in_title] == '\\') || (in_title_id[idx_in_title] == '/') || (in_title_id[idx_in_title] == ':')) { + idx_out_title = 0; + } else if (in_title_id[idx_in_title] == '_') { + out_title_id[idx_out_title++] = '-'; + } else if (in_title_id[idx_in_title] != '.') { + out_title_id[idx_out_title++] = in_title_id[idx_in_title]; + } else { + } + idx_in_title++; + } +} + +void game_names_get_name_by_folder(const char* const folder, char* const game_name) { + strlcpy(game_name, "", MAX_GAME_NAME_LENGTH); + if (game_names_sanity_check_title_id(folder)) { + game_lookup game; + + game = find_game_lookup(folder); + + if (game.game_id != 0) { + strlcpy(game_name, game.name, MAX_GAME_NAME_LENGTH); + } + } else { + int dir_fd, it_fd = -1; + char filename[MAX_GAME_NAME_LENGTH] = {}; + char dir[64]; + if (settings_get_mode() == MODE_PS1) { + snprintf(dir, sizeof(dir), "MemoryCards/PS1/%s", folder); + } else { + snprintf(dir, sizeof(dir), "MemoryCards/PS2/%s", folder); + } + + dir_fd = sd_open(dir, O_RDONLY); + if (dir_fd >= 0) { + it_fd = sd_iterate_dir(dir_fd, it_fd); + + while (it_fd != -1) { + sd_get_name(it_fd, filename, MAX_GAME_NAME_LENGTH); + + if (file_has_extension(filename, ".txt")) { + file_remove_extension(filename); + strlcpy(game_name, filename, MAX_GAME_NAME_LENGTH); + break; + } + it_fd = sd_iterate_dir(dir_fd, it_fd); + } + if (it_fd != -1) + sd_close(it_fd); + sd_close(dir_fd); + } + } +} + +void game_names_get_parent(const char* const game_id, char* const parent_id) { + char prefixString[MAX_PREFIX_LENGTH] = {}; + char idString[10] = {}; + game_lookup game; + + if (game_id != NULL && game_id[0]) { + char* copy = strdup(game_id); + char* split = strtok(copy, "-"); + + if (strlen(split) > 0) { + strlcpy(prefixString, split, MAX_PREFIX_LENGTH); + for (uint8_t i = 0; i < MAX_PREFIX_LENGTH - 1; i++) { + prefixString[i] = toupper((unsigned char)prefixString[i]); + } + } + split = strtok(NULL, "-"); + + if (strlen(split) > 0) { + strlcpy(idString, split, 11); + } + } + + game = find_game_lookup(game_id); + + if (game.game_id != 0) { + snprintf(parent_id, MAX_GAME_ID_LENGTH, "%s-%0*d", prefixString, (int)strlen(idString), (int)game.parent_id); + + debug_printf("Parent ID: %s\n", parent_id); + } else { + strlcpy(parent_id, game_id, MAX_GAME_ID_LENGTH); + } +} + diff --git a/src/game_names/game_names.h b/src/game_names/game_names.h new file mode 100644 index 0000000..a1dedfb --- /dev/null +++ b/src/game_names/game_names.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +void game_names_extract_title_id(const uint8_t* const in_title_id, char* const out_title_id, const size_t in_title_id_length, const size_t out_buffer_size); +bool game_names_sanity_check_title_id(const char* const title_id); + +void game_names_get_name_by_folder(const char* const folder, char* const game_name); +void game_names_get_parent(const char* const game_id, char* const parent_id); + +void game_names_update_card_dir(const char* const dir); +void game_names_init(void); + +void game_names_set_game_id(const char* const game_id); +const char* game_names_get_game_id(void); + +const char* game_names_get_game_name(void); +const char* game_names_name_from_file(char* path, int channel); diff --git a/src/gui.c b/src/gui.c index 1b14170..31aa70e 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1,35 +1,32 @@ #include "gui.h" +#include #include #include #include "config.h" -#include "lvgl.h" #include "input.h" -#include "ui_menu.h" #include "keystore.h" -#include "settings.h" +#include "lvgl.h" #include "oled.h" - #include "ps1/ps1_cardman.h" -#include "ps1/ps1_odeman.h" #include "ps1/ps1_memory_card.h" - -#include "ps2/ps2_memory_card.h" +#include "ps1/ps1_odeman.h" +#include "ps2/card_emu/ps2_memory_card.h" #include "ps2/ps2_cardman.h" #include "ps2/ps2_dirty.h" #include "ps2/ps2_exploit.h" - -#include "version/version.h" - +#include "settings.h" +#include "ui_menu.h" #include "ui_theme_mono.h" +#include "version/version.h" /* Displays the line at the bottom for long pressing buttons */ static lv_obj_t *g_navbar, *g_progress_bar, *g_progress_text, *g_activity_frame; static lv_obj_t *scr_switch_nag, *scr_card_switch, *scr_main, *scr_menu, *scr_freepsxboot, *menu, *main_page; static lv_style_t style_inv; -static lv_obj_t *scr_main_idx_lbl, *scr_main_channel_lbl,*src_main_title_lbl, *lbl_civ_err, *lbl_autoboot, *lbl_channel; +static lv_obj_t *scr_main_idx_lbl, *scr_main_channel_lbl, *src_main_title_lbl, *lbl_civ_err, *lbl_autoboot, *lbl_channel; static int have_oled; static int switching_card; @@ -38,25 +35,25 @@ static int terminated; static bool refresh_gui; static bool installing_exploit; -#define COLOR_FG lv_color_white() -#define COLOR_BG lv_color_black() +#define COLOR_FG lv_color_white() +#define COLOR_BG lv_color_black() -static lv_obj_t* ui_scr_create(void) { - lv_obj_t * obj = lv_obj_create(NULL); +static lv_obj_t *ui_scr_create(void) { + lv_obj_t *obj = lv_obj_create(NULL); lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); lv_group_add_obj(lv_group_get_default(), obj); return obj; } /* create a navigatable UI menu container, so that the item (label) inside can be selected and clicked */ -static lv_obj_t* ui_menu_cont_create_nav(lv_obj_t *parent) { - lv_obj_t* cont = ui_menu_cont_create(parent); +static lv_obj_t *ui_menu_cont_create_nav(lv_obj_t *parent) { + lv_obj_t *cont = ui_menu_cont_create(parent); lv_obj_add_flag(cont, LV_OBJ_FLAG_EVENT_BUBBLE); lv_group_add_obj(lv_group_get_default(), cont); return cont; } -static lv_obj_t* ui_menu_subpage_create(lv_obj_t *menu, const char* title) { +static lv_obj_t *ui_menu_subpage_create(lv_obj_t *menu, const char *title) { lv_obj_t *page = ui_menu_page_create(menu, title); lv_obj_add_flag(page, LV_OBJ_FLAG_EVENT_BUBBLE); lv_group_add_obj(lv_group_get_default(), page); @@ -64,7 +61,7 @@ static lv_obj_t* ui_menu_subpage_create(lv_obj_t *menu, const char* title) { return page; } -static lv_obj_t* ui_label_create(lv_obj_t *parent, const char *text) { +static lv_obj_t *ui_label_create(lv_obj_t *parent, const char *text) { lv_obj_t *label = lv_label_create(parent); lv_label_set_text(label, text); return label; @@ -77,7 +74,7 @@ static lv_obj_t *ui_label_create_at(lv_obj_t *parent, int x, int y, const char * return label; } -static lv_obj_t* ui_label_create_grow(lv_obj_t *parent, const char *text) { +static lv_obj_t *ui_label_create_grow(lv_obj_t *parent, const char *text) { lv_obj_t *label = ui_label_create(parent, text); lv_obj_set_flex_grow(label, 1); return label; @@ -96,13 +93,13 @@ static void ui_make_scrollable(lv_obj_t *cont, lv_obj_t *label) { lv_obj_add_event_cb(cont, scrollable_label, LV_EVENT_DEFOCUSED, label); } -static lv_obj_t* ui_label_create_grow_scroll(lv_obj_t *parent, const char *text) { +static lv_obj_t *ui_label_create_grow_scroll(lv_obj_t *parent, const char *text) { lv_obj_t *label = ui_label_create_grow(parent, text); ui_make_scrollable(parent, label); return label; } -static lv_obj_t* ui_header_create(lv_obj_t *parent, const char *text) { +static lv_obj_t *ui_header_create(lv_obj_t *parent, const char *text) { lv_obj_t *lbl = lv_label_create(parent); lv_obj_set_align(lbl, LV_ALIGN_TOP_MID); lv_obj_add_style(lbl, &style_inv, 0); @@ -116,8 +113,8 @@ static void flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t if (have_oled) { oled_clear(); - for(int y = area->y1; y <= area->y2; y++) { - for(int x = area->x1; x <= area->x2; x++) { + for (int y = area->y1; y <= area->y2; y++) { + for (int x = area->x1; x <= area->x2; x++) { if (color_p->full) oled_draw_pixel(x, y); color_p++; @@ -159,7 +156,7 @@ static void create_nav(void) { g_activity_frame = lv_line_create(lv_layer_top()); lv_obj_add_style(g_activity_frame, &style_frame, 0); - static lv_point_t line_points[5] = { {0,0}, {DISPLAY_WIDTH,0}, {DISPLAY_WIDTH,DISPLAY_HEIGHT}, {0,DISPLAY_HEIGHT}, {0,0} }; + static lv_point_t line_points[5] = {{0, 0}, {DISPLAY_WIDTH, 0}, {DISPLAY_WIDTH, DISPLAY_HEIGHT}, {0, DISPLAY_HEIGHT}, {0, 0}}; lv_line_set_points(g_activity_frame, line_points, 5); lv_obj_add_flag(g_activity_frame, LV_OBJ_FLAG_HIDDEN); @@ -180,10 +177,10 @@ static void gui_tick(void) { } static void reload_card_cb(int progress) { - static lv_point_t line_points[2] = { {0, DISPLAY_HEIGHT/2}, {0, DISPLAY_HEIGHT/2} }; + static lv_point_t line_points[2] = {{0, DISPLAY_HEIGHT / 2}, {0, DISPLAY_HEIGHT / 2}}; static int prev_progress; progress += 5; - if (progress/5 == prev_progress/5) + if (progress / 5 == prev_progress / 5) return; prev_progress = progress; line_points[1].x = DISPLAY_WIDTH * progress / 100; @@ -215,20 +212,12 @@ static void evt_scr_main(lv_event_t *event) { if (settings_get_mode() == MODE_PS1) { prevChannel = ps1_cardman_get_channel(); prevIdx = ps1_cardman_get_idx(); - + switch (key) { - case INPUT_KEY_PREV: - ps1_cardman_prev_channel(); - break; - case INPUT_KEY_NEXT: - ps1_cardman_next_channel(); - break; - case INPUT_KEY_BACK: - ps1_cardman_prev_idx(); - break; - case INPUT_KEY_ENTER: - ps1_cardman_next_idx(); - break; + case INPUT_KEY_PREV: ps1_cardman_prev_channel(); break; + case INPUT_KEY_NEXT: ps1_cardman_next_channel(); break; + case INPUT_KEY_BACK: ps1_cardman_prev_idx(); break; + case INPUT_KEY_ENTER: ps1_cardman_next_idx(); break; } if ((prevChannel != ps1_cardman_get_channel()) || (prevIdx != ps1_cardman_get_idx())) { ps1_memory_card_exit(); @@ -237,25 +226,18 @@ static void evt_scr_main(lv_event_t *event) { printf("new PS1 card=%d chan=%d\n", ps1_cardman_get_idx(), ps1_cardman_get_channel()); } } else { + ps2_cardman_state_t prevState = ps2_cardman_get_state(); prevChannel = ps2_cardman_get_channel(); prevIdx = ps2_cardman_get_idx(); switch (key) { - case INPUT_KEY_PREV: - ps2_cardman_prev_channel(); - break; - case INPUT_KEY_NEXT: - ps2_cardman_next_channel(); - break; - case INPUT_KEY_BACK: - ps2_cardman_prev_idx(); - break; - case INPUT_KEY_ENTER: - ps2_cardman_next_idx(); - break; + case INPUT_KEY_PREV: ps2_cardman_prev_channel(); break; + case INPUT_KEY_NEXT: ps2_cardman_next_channel(); break; + case INPUT_KEY_BACK: ps2_cardman_prev_idx(); break; + case INPUT_KEY_ENTER: ps2_cardman_next_idx(); break; } - if ((prevChannel != ps2_cardman_get_channel()) || (prevIdx != ps2_cardman_get_idx())) { + if ((prevChannel != ps2_cardman_get_channel()) || (prevIdx != ps2_cardman_get_idx()) || (prevState != ps2_cardman_get_state())) { ps2_memory_card_exit(); ps2_cardman_close(); switching_card = 1; @@ -267,7 +249,6 @@ static void evt_scr_main(lv_event_t *event) { switching_card_timeout = time_us_64() + 1500 * 1000; } } - } } @@ -377,7 +358,6 @@ static void create_main_screen(void) { ui_header_create(scr_main, "PS2 Memory Card"); } - ui_label_create_at(scr_main, 0, 24, "Card"); scr_main_idx_lbl = ui_label_create_at(scr_main, 0, 24, ""); @@ -467,7 +447,7 @@ static void create_cardswitch_screen(void) { g_progress_text = lv_label_create(scr_card_switch); lv_obj_set_align(g_progress_text, LV_ALIGN_TOP_LEFT); - lv_obj_set_pos(g_progress_text, 0, DISPLAY_HEIGHT-9); + lv_obj_set_pos(g_progress_text, 0, DISPLAY_HEIGHT - 9); lv_label_set_text(g_progress_text, "Read XXX kB/s"); } @@ -638,7 +618,6 @@ static void create_menu_screen(void) { ui_label_create_grow_scroll(cont, "Info"); ui_label_create(cont, ">"); ui_menu_set_load_page_event(menu, cont, info_page); - } ui_menu_set_page(menu, main_page); @@ -707,8 +686,7 @@ void gui_init(void) { installing_exploit = false; } -void gui_request_refresh(void) -{ +void gui_request_refresh(void) { refresh_gui = true; } @@ -742,6 +720,9 @@ void gui_do_ps2_card_switch(void) { void gui_task(void) { input_update_display(g_navbar); + char card_name[127]; + const char *folder_name = NULL; + if (settings_get_mode() == MODE_PS1) { static int displayed_card_idx = -1; static int displayed_card_channel = -1; @@ -751,25 +732,26 @@ void gui_task(void) { if (displayed_card_idx != ps1_cardman_get_idx() || displayed_card_channel != ps1_cardman_get_channel() || refresh_gui) { displayed_card_idx = ps1_cardman_get_idx(); displayed_card_channel = ps1_cardman_get_channel(); + folder_name = ps1_cardman_get_folder_name(); + snprintf(card_channel_s, sizeof(card_channel_s), "%d", displayed_card_channel); - if (displayed_card_idx == 0) { - const char* id = ps1_cardman_get_gameid(); - const char* name = ps1_cardman_get_gamename(); - lv_label_set_text(scr_main_idx_lbl, id); - if (name[0] != 0x00) - { - lv_label_set_text(src_main_title_lbl, name); - } - else - { - lv_label_set_text(src_main_title_lbl, ""); - } - } - else { + + if (displayed_card_idx > 0) { snprintf(card_idx_s, sizeof(card_idx_s), "%d", displayed_card_idx); lv_label_set_text(scr_main_idx_lbl, card_idx_s); + } else { + lv_label_set_text(scr_main_idx_lbl, folder_name); + } + + memset(card_name, 0, sizeof(card_name)); + game_names_get_name_by_folder(folder_name, card_name); + + if (card_name[0]) { + lv_label_set_text(src_main_title_lbl, card_name); + } else { lv_label_set_text(src_main_title_lbl, ""); } + lv_label_set_text(scr_main_channel_lbl, card_channel_s); } @@ -781,23 +763,45 @@ void gui_task(void) { } else { static int displayed_card_idx = -1; static int displayed_card_channel = -1; + static ps2_cardman_state_t cardman_state = PS2_CM_STATE_NORMAL; static char card_idx_s[8]; static char card_channel_s[8]; - if (displayed_card_idx != ps2_cardman_get_idx() || displayed_card_channel != ps2_cardman_get_channel()) { + if (displayed_card_idx != ps2_cardman_get_idx() || displayed_card_channel != ps2_cardman_get_channel() || cardman_state != ps2_cardman_get_state() || + refresh_gui) { displayed_card_idx = ps2_cardman_get_idx(); displayed_card_channel = ps2_cardman_get_channel(); - if (displayed_card_idx == 0) { + folder_name = ps2_cardman_get_folder_name(); + cardman_state = ps2_cardman_get_state(); + + if (PS2_CM_STATE_BOOT == cardman_state) { snprintf(card_idx_s, sizeof(card_idx_s), "BOOT"); snprintf(card_channel_s, sizeof(card_channel_s), " "); lv_label_set_text(lbl_channel, ""); - + lv_label_set_text(scr_main_idx_lbl, card_idx_s); + } else if (PS2_CM_STATE_GAMEID == cardman_state) { + lv_label_set_text(scr_main_idx_lbl, folder_name); + snprintf(card_channel_s, sizeof(card_channel_s), "%d", displayed_card_channel); + lv_label_set_text(lbl_channel, "Channel"); + lv_label_set_text(scr_main_idx_lbl, folder_name); } else { snprintf(card_idx_s, sizeof(card_idx_s), "%d", displayed_card_idx); snprintf(card_channel_s, sizeof(card_channel_s), "%d", displayed_card_channel); lv_label_set_text(lbl_channel, "Channel"); + lv_label_set_text(scr_main_idx_lbl, card_idx_s); + } + + memset(card_name, 0, sizeof(card_name)); + game_names_get_name_by_folder(folder_name, card_name); + + if (card_name[0]) { + lv_label_set_text(src_main_title_lbl, card_name); + } else { + lv_label_set_text(src_main_title_lbl, ""); } - lv_label_set_text(scr_main_idx_lbl, card_idx_s); + lv_label_set_text(scr_main_channel_lbl, card_channel_s); + + refresh_gui = false; } if (switching_card && switching_card_timeout < time_us_64() && !input_is_any_down()) { diff --git a/src/main.c b/src/main.c index 5748564..fe2440c 100644 --- a/src/main.c +++ b/src/main.c @@ -22,12 +22,14 @@ #include "ps1/ps1_cardman.h" #include "ps1/ps1_odeman.h" -#include "ps2/ps2_memory_card.h" #include "ps2/ps2_dirty.h" +#include "ps2/card_emu/ps2_memory_card.h" #include "ps2/ps2_cardman.h" #include "ps2/ps2_psram.h" #include "ps2/ps2_exploit.h" +#include "ps2/card_emu/ps2_sd2psxman.h" + /* reboot to bootloader if either button is held on startup to make the device easier to flash when assembled inside case */ static void check_bootloader_reset(void) { @@ -130,6 +132,7 @@ int main() { while (1) { debug_task(); + ps2_sd2psxman_task(); ps2_dirty_task(); gui_task(); input_task(); diff --git a/src/ps1/CMakeLists.txt b/src/ps1/CMakeLists.txt new file mode 100644 index 0000000..4dadd61 --- /dev/null +++ b/src/ps1/CMakeLists.txt @@ -0,0 +1,18 @@ +add_library(ps1_card STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/ps1_cardman.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps1_dirty.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps1_memory_card.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps1_empty_card.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps1_odeman.c +) + +target_include_directories(ps1_card PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(ps1_card + PRIVATE + sd2psx_common + pico_platform_headers + pico_stdlib + hardware_pio) + +pico_generate_pio_header(ps1_card ${CMAKE_CURRENT_LIST_DIR}/ps1_mc_spi.pio) diff --git a/src/ps1/ps1_cardman.c b/src/ps1/ps1_cardman.c index 010b381..06ca8d3 100644 --- a/src/ps1/ps1_cardman.c +++ b/src/ps1/ps1_cardman.c @@ -1,6 +1,7 @@ #include "ps1_cardman.h" #include +#include #include #include #include @@ -11,6 +12,8 @@ #include "bigmem.h" #include "ps1_empty_card.h" +#include "game_names/game_names.h" + #include "hardware/timer.h" #define CARD_SIZE (128 * 1024) @@ -23,141 +26,17 @@ static int fd = -1; #define CHAN_MIN 1 #define CHAN_MAX 8 -#define MAX_GAME_NAME_LENGTH (127) -#define MAX_PREFIX_LENGTH (4) #define MAX_GAME_ID_LENGTH (16) -extern const char _binary_gamedbps1_dat_start, _binary_gamedbps1_dat_size; - static int card_idx; static int card_chan; -static char card_game_id[MAX_GAME_ID_LENGTH]; -static const char* card_game_name; - -static bool ps1_cardman_sanity_check_game_id(const char* const game_id) { - uint8_t i = 0U; - - char splittable_game_id[MAX_GAME_ID_LENGTH]; - strlcpy(splittable_game_id, game_id, MAX_GAME_ID_LENGTH); - char* prefix = strtok(splittable_game_id, "-"); - char* id = strtok(NULL, "-"); - - while (prefix[i] != 0x00) { - if (!isalpha((int)prefix[i])) { - return false; - } - i++; - } - if (i == 0) { - return false; - } else { - i = 0; - } - - while (prefix[i] != 0x00) { - if (!isdigit((int)id[i])) { - return false; - } - i++; - } - - return (i > 0); -} - -#pragma GCC diagnostic ignored "-Warray-bounds" -static uint32_t ps1_cardman_char_array_to_uint32(const char in[4]) { - char inter[4] = {in[3], in[2], in[1], in[0]}; - uint32_t tgt; - memcpy(&tgt, inter, sizeof(tgt)); - return tgt; -} -#pragma GCC diagnostic pop - -static uint32_t ps1_cardman_find_prefix_offset(uint32_t numericPrefix) { - uint32_t offset = 0; - - const char* pointer = &_binary_gamedbps1_dat_start; - - while (offset == 0) { - uint32_t currentprefix = ps1_cardman_char_array_to_uint32(pointer), currentoffset = ps1_cardman_char_array_to_uint32(&pointer[4]); - - if (currentprefix == numericPrefix) { - offset = currentoffset; - } - if ((currentprefix == 0U) && (currentoffset == 0U)) { - break; - } - pointer += 8; - } - - return offset; -} - -static bool ps1_cardman_update_game_data(const char* const id) { - char prefixString[MAX_PREFIX_LENGTH + 1] = {}; - char idString[10] = {}; - - uint32_t numericPrefix = 0, prefixOffset = 0, currentId = 0, numericId = 0; - - if (ps1_cardman_sanity_check_game_id(id)) { - char* copy = strdup(id); - char* split = strtok(copy, "-"); - - if (strlen(split) > 0) { - strlcpy(prefixString, split, MAX_PREFIX_LENGTH + 1); - for (uint8_t i = 0; i < MAX_PREFIX_LENGTH; i++) { - prefixString[i] = toupper((unsigned char)prefixString[i]); - } - numericPrefix = ps1_cardman_char_array_to_uint32(prefixString); - } - - split = strtok(NULL, "-"); - - if (strlen(split) > 0) { - strlcpy(idString, split, 11); - numericId = atoi(idString); - } - - prefixOffset = ps1_cardman_find_prefix_offset(numericPrefix); - - if (prefixOffset < (size_t)&_binary_gamedbps1_dat_size) { - uint32_t offset = prefixOffset; - do { - currentId = ps1_cardman_char_array_to_uint32(&(&_binary_gamedbps1_dat_start)[offset]); - if (currentId == numericId) { - uint32_t name_offset = ps1_cardman_char_array_to_uint32(&(&_binary_gamedbps1_dat_start)[offset + 4]); - - debug_printf("Found ID - Name Offset: %d, Parent ID: %d\n", (int)name_offset, - (int)ps1_cardman_char_array_to_uint32(&(&_binary_gamedbps1_dat_start)[offset + 8])); - - snprintf(card_game_id, MAX_GAME_ID_LENGTH, "%s-%0*d", prefixString, (int)strlen(idString), - (int)ps1_cardman_char_array_to_uint32(&(&_binary_gamedbps1_dat_start)[offset + 8])); - - debug_printf("Parent ID: %s\n", card_game_id); - - if ((name_offset < (size_t)&_binary_gamedbps1_dat_size) && (&_binary_gamedbps1_dat_start + name_offset) != 0x00) { - card_game_name = (&_binary_gamedbps1_dat_start + name_offset); - debug_printf("Name:%s\n", card_game_name); - - return true; - } - else - { - return false; - } - } - offset += 12; - } while (currentId != 0); - } - } - - return false; -} +static char folder_name[MAX_GAME_ID_LENGTH]; void ps1_cardman_init(void) { card_idx = settings_get_ps1_card(); card_chan = settings_get_ps1_channel(); - memset(card_game_id, 0, sizeof(card_game_id)); + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); + } int ps1_cardman_write_sector(int sector, void *buf512) { @@ -180,12 +59,9 @@ void ps1_cardman_flush(void) { static void ensuredirs(void) { char cardpath[32]; - if ((card_game_id[0] != 0x00) && (card_idx < IDX_MIN) ) { - snprintf(cardpath, sizeof(cardpath), "MemoryCards/PS1/%s", card_game_id); - } else { - snprintf(cardpath, sizeof(cardpath), "MemoryCards/PS1/Card%d", card_idx); - } - + + snprintf(cardpath, sizeof(cardpath), "MemoryCards/PS1/%s", folder_name); + sd_mkdir("MemoryCards"); sd_mkdir("MemoryCards/PS1"); sd_mkdir(cardpath); @@ -203,12 +79,10 @@ static void genblock(size_t pos, void *buf) { void ps1_cardman_open(void) { char path[64]; - ensuredirs(); - if ((card_game_id[0] != 0x00) && (card_idx < IDX_MIN)) { - snprintf(path, sizeof(path), "MemoryCards/PS1/%s/%s-%d.mcd", card_game_id, card_game_id, card_chan); - } else { - snprintf(path, sizeof(path), "MemoryCards/PS1/Card%d/Card%d-%d.mcd", card_idx, card_idx, card_chan); + + snprintf(path, sizeof(path), "MemoryCards/PS1/%s/%s-%d.mcd", folder_name, folder_name, card_chan); + if (card_idx != IDX_GAMEID) { /* this is ok to do on every boot because it wouldn't update if the value is the same as currently stored */ settings_set_ps1_card(card_idx); settings_set_ps1_channel(card_chan); @@ -283,16 +157,35 @@ void ps1_cardman_prev_channel(void) { void ps1_cardman_next_idx(void) { card_idx += 1; card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); + + if (card_idx == IDX_GAMEID) { + const char* const game_id = ps1_memory_card_get_game_id(); + if ((game_id != NULL) && (game_id[0])) + snprintf(folder_name, sizeof(folder_name), "%s", game_id); + else + card_idx = 1; + } + } void ps1_cardman_prev_idx(void) { card_idx -= 1; card_chan = CHAN_MIN; - if ((card_idx == IDX_GAMEID) - && (card_game_id[0] == 0x00)) - card_idx = IDX_MIN; - else if (card_idx < IDX_GAMEID) + if (card_idx < IDX_GAMEID) card_idx = IDX_GAMEID; + + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); + + if (card_idx == IDX_GAMEID) { + char parent_id[MAX_GAME_ID_LENGTH]; + const char* const game_id = ps1_memory_card_get_game_id(); + game_names_get_parent(game_id, parent_id); + if ((game_id != NULL) && (game_id[0])) + snprintf(folder_name, sizeof(folder_name), "%s", parent_id); + else + card_idx = IDX_MIN; + } } int ps1_cardman_get_idx(void) { @@ -303,25 +196,16 @@ int ps1_cardman_get_channel(void) { return card_chan; } -void ps1_cardman_set_gameid(const char* game_id) { - if (!ps1_cardman_update_game_data(game_id)) - { - strlcpy(card_game_id, game_id, sizeof(card_game_id)); - card_game_name = NULL; - card_idx = IDX_MIN; - card_chan = CHAN_MIN; - } - else - { +void ps1_cardman_set_ode_idx(const char* const card_game_id) { + if (card_game_id[0]) { + char parent_id[MAX_GAME_ID_LENGTH]; + game_names_get_parent(card_game_id, parent_id); + snprintf(folder_name, sizeof(folder_name), "%s", parent_id); card_idx = IDX_GAMEID; card_chan = CHAN_MIN; } } -const char* ps1_cardman_get_gameid(void) { - return card_game_id; -} - -const char* ps1_cardman_get_gamename(void) { - return card_game_name; +const char* ps1_cardman_get_folder_name(void) { + return folder_name; } \ No newline at end of file diff --git a/src/ps1/ps1_cardman.h b/src/ps1/ps1_cardman.h index 16f3c3f..9ac14d4 100644 --- a/src/ps1/ps1_cardman.h +++ b/src/ps1/ps1_cardman.h @@ -7,12 +7,10 @@ void ps1_cardman_open(void); void ps1_cardman_close(void); int ps1_cardman_get_idx(void); int ps1_cardman_get_channel(void); +const char* ps1_cardman_get_folder_name(void); void ps1_cardman_next_channel(void); void ps1_cardman_prev_channel(void); void ps1_cardman_next_idx(void); void ps1_cardman_prev_idx(void); - -void ps1_cardman_set_gameid(const char* game_id); -const char* ps1_cardman_get_gameid(void); -const char* ps1_cardman_get_gamename(void); +void ps1_cardman_set_ode_idx(const char* const card_game_id); diff --git a/src/ps1/ps1_memory_card.c b/src/ps1/ps1_memory_card.c index f0a3f48..bd44e5a 100644 --- a/src/ps1/ps1_memory_card.c +++ b/src/ps1/ps1_memory_card.c @@ -2,14 +2,15 @@ #include "hardware/timer.h" #include "pico/platform.h" #include "string.h" +#include #include "config.h" #include "ps1_mc_spi.pio.h" #include "debug.h" #include "bigmem.h" #include "ps1_dirty.h" -#include -#include +#include "ps1/ps1_memory_card.h" +#include "game_names/game_names.h" #define card_image bigmem.ps1.card_image @@ -34,30 +35,7 @@ static pio_t cmd_reader, dat_writer; static volatile int mc_exit_request, mc_exit_response, mc_enter_request, mc_enter_response; -static void __time_critical_func(clean_title_id)(const uint8_t* const in_title_id, char* const out_title_id, const size_t in_title_id_length, const size_t out_buffer_size) { - uint16_t idx_in_title = 0, idx_out_title = 0; - - while ( (in_title_id[idx_in_title] != 0x00) - && (idx_in_title < in_title_id_length) - && (idx_out_title < out_buffer_size) ) { - if ((in_title_id[idx_in_title] == ';') || (in_title_id[idx_in_title] == 0x00)) { - out_title_id[idx_out_title++] = 0x00; - break; - } else if ((in_title_id[idx_in_title] == '\\') || (in_title_id[idx_in_title] == '/') || (in_title_id[idx_in_title] == ':')) { - idx_out_title = 0; - } else if (in_title_id[idx_in_title] == '_') { - out_title_id[idx_out_title++] = '-'; - } else if (in_title_id[idx_in_title] != '.') { - out_title_id[idx_out_title++] = in_title_id[idx_in_title]; - } else { - } - idx_in_title++; - } -} - static void __time_critical_func(reset_pio)(void) { - // debug_printf("!!\n"); - pio_set_sm_mask_enabled(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm), false); pio_restart_sm_mask(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm)); @@ -196,8 +174,11 @@ static int __time_critical_func(mc_do_state)(uint8_t ch) { } else if (cmd == 0x21) { // MCP Game ID if (byte_count == game_id_length + 4) { - clean_title_id(&payload[4], received_game_id, game_id_length, sizeof(received_game_id)); - mc_pro_command = MCP_GAME_ID; + game_names_extract_title_id(&payload[4], received_game_id, game_id_length, sizeof(received_game_id)); + if (game_names_sanity_check_title_id(received_game_id)) + mc_pro_command = MCP_GAME_ID; + else + memset(received_game_id, 0, sizeof(received_game_id)); } switch (byte_count) { case 2: memset(received_game_id, 0, sizeof(received_game_id)); return 0x00; @@ -369,8 +350,6 @@ void ps1_memory_card_enter(void) { mc_enter_request = mc_enter_response = 0; memcard_running = 1; mc_pro_command = 0; - game_id_length = sizeof(received_game_id); - memset(received_game_id, 0, sizeof(received_game_id)); } void ps1_memory_card_reset_ode_command(void) { diff --git a/src/ps1/ps1_odeman.c b/src/ps1/ps1_odeman.c index 0bc0e41..c21cd44 100644 --- a/src/ps1/ps1_odeman.c +++ b/src/ps1/ps1_odeman.c @@ -5,15 +5,12 @@ #include "ps1_odeman.h" #include "debug.h" +#include #include #include #define CARD_SWITCH_DELAY_MS (250) - - -void ps1_odeman_init(void) { - -} +#define MAX_GAME_ID_LENGTH (16) void ps1_odeman_task(void) { uint8_t ode_command = ps1_memory_card_get_ode_command(); @@ -28,7 +25,7 @@ void ps1_odeman_task(void) { const char *game_id; game_id = ps1_memory_card_get_game_id(); debug_printf("Received Game ID: %s\n", game_id); - ps1_cardman_set_gameid(game_id); + ps1_cardman_set_ode_idx(game_id); break; } case MCP_NXT_CARD: @@ -54,10 +51,8 @@ void ps1_odeman_task(void) { sleep_ms(CARD_SWITCH_DELAY_MS); // This delay is required, so ODE can register the card change - ps1_cardman_open(); ps1_memory_card_enter(); gui_request_refresh(); } - -} \ No newline at end of file +} diff --git a/src/ps2/CMakeLists.txt b/src/ps2/CMakeLists.txt new file mode 100644 index 0000000..5fc6164 --- /dev/null +++ b/src/ps2/CMakeLists.txt @@ -0,0 +1,30 @@ + +add_library(ps2_card STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_sd2psxman.c + ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_sd2psxman_commands.c + ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_memory_card.c + ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_mc_commands.c + ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_mc_auth.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps2_dirty.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps2_cardman.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps2_pio_qspi.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps2_psram.c + ${CMAKE_CURRENT_SOURCE_DIR}/ps2_exploit.c + ) + +target_include_directories(ps2_card + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(ps2_card PRIVATE + sd2psx_common + pico_stdlib + pico_multicore + hardware_sync + hardware_pio + hardware_flash + hardware_dma) + + +pico_generate_pio_header(ps2_card ${CMAKE_CURRENT_LIST_DIR}/card_emu/ps2_mc_spi.pio) +pico_generate_pio_header(ps2_card ${CMAKE_CURRENT_LIST_DIR}/ps2_qspi.pio) diff --git a/src/ps2/card_emu/ps2_mc_auth.c b/src/ps2/card_emu/ps2_mc_auth.c new file mode 100644 index 0000000..806d03f --- /dev/null +++ b/src/ps2/card_emu/ps2_mc_auth.c @@ -0,0 +1,505 @@ + +#include "ps2_mc_auth.h" + +#include + +#include "debug.h" +#include "des.h" +#include "keystore.h" +#include "ps2_mc_internal.h" + +// keysource and key are self generated values +uint8_t keysource[] = {0xf5, 0x80, 0x95, 0x3c, 0x4c, 0x84, 0xa9, 0xc0}; +uint8_t dex_key[16] = {0x17, 0x39, 0xd3, 0xbc, 0xd0, 0x2c, 0x18, 0x07, 0x4b, 0x17, 0xf0, 0xea, 0xc4, 0x66, 0x30, 0xf9}; +uint8_t cex_key[16] = {0x06, 0x46, 0x7a, 0x6c, 0x5b, 0x9b, 0x82, 0x77, 0x0d, 0xdf, 0xe9, 0x7e, 0x24, 0x5b, 0x9f, 0xca}; +uint8_t *key = cex_key; + +uint8_t iv[8]; +uint8_t seed[8]; +uint8_t nonce[8]; +uint8_t MechaChallenge3[8]; +uint8_t MechaChallenge2[8]; +uint8_t MechaChallenge1[8]; +uint8_t CardResponse1[8]; +uint8_t CardResponse2[8]; +uint8_t CardResponse3[8]; +uint8_t hostkey[9]; + +void __time_critical_func(desEncrypt)(void *key, void *data) { + DesContext dc; + desInit(&dc, (uint8_t *)key, 8); + desEncryptBlock(&dc, (uint8_t *)data, (uint8_t *)data); +} + +void __time_critical_func(desDecrypt)(void *key, void *data) { + DesContext dc; + desInit(&dc, (uint8_t *)key, 8); + desDecryptBlock(&dc, (uint8_t *)data, (uint8_t *)data); +} + +void __time_critical_func(doubleDesEncrypt)(void *key, void *data) { + desEncrypt(key, data); + desDecrypt(&((uint8_t *)key)[8], data); + desEncrypt(key, data); +} + +void __time_critical_func(doubleDesDecrypt)(void *key, void *data) { + desDecrypt(key, data); + desEncrypt(&((uint8_t *)key)[8], data); + desDecrypt(key, data); +} + +void __time_critical_func(xor_bit)(const void *a, const void *b, void *Result, size_t Length) { + size_t i; + for (i = 0; i < Length; i++) { + ((uint8_t *)Result)[i] = ((uint8_t *)a)[i] ^ ((uint8_t *)b)[i]; + } +} + +void __time_critical_func(generateIvSeedNonce)() { + for (int i = 0; i < 8; i++) { + iv[i] = 0x42; + seed[i] = keysource[i] ^ iv[i]; + nonce[i] = 0x42; + } +} + +void __time_critical_func(generateResponse)() { + doubleDesDecrypt(key, MechaChallenge1); + uint8_t random[8] = {0}; + xor_bit(MechaChallenge1, ps2_civ, random, 8); + + // MechaChallenge2 and MechaChallenge3 let's the card verify the console + + xor_bit(nonce, ps2_civ, CardResponse1, 8); + + doubleDesEncrypt(key, CardResponse1); + + xor_bit(random, CardResponse1, CardResponse2, 8); + doubleDesEncrypt(key, CardResponse2); + + uint8_t CardKey[] = {'M', 'e', 'c', 'h', 'a', 'P', 'w', 'n'}; + xor_bit(CardKey, CardResponse2, CardResponse3, 8); + doubleDesEncrypt(key, CardResponse3); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_probe(void) { + uint8_t _; + /* probe support ? */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_getIv(void) { + uint8_t _; + debug_printf("iv : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(iv)); + + /* get IV */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(iv[7]); + receiveOrNextCmd(&_); + mc_respond(iv[6]); + receiveOrNextCmd(&_); + mc_respond(iv[5]); + receiveOrNextCmd(&_); + mc_respond(iv[4]); + receiveOrNextCmd(&_); + mc_respond(iv[3]); + receiveOrNextCmd(&_); + mc_respond(iv[2]); + receiveOrNextCmd(&_); + mc_respond(iv[1]); + receiveOrNextCmd(&_); + mc_respond(iv[0]); + receiveOrNextCmd(&_); + mc_respond(XOR8(iv)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_getSeed(void) { + uint8_t _; + debug_printf("seed : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(seed)); + + /* get seed */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(seed[7]); + receiveOrNextCmd(&_); + mc_respond(seed[6]); + receiveOrNextCmd(&_); + mc_respond(seed[5]); + receiveOrNextCmd(&_); + mc_respond(seed[4]); + receiveOrNextCmd(&_); + mc_respond(seed[3]); + receiveOrNextCmd(&_); + mc_respond(seed[2]); + receiveOrNextCmd(&_); + mc_respond(seed[1]); + receiveOrNextCmd(&_); + mc_respond(seed[0]); + receiveOrNextCmd(&_); + mc_respond(XOR8(seed)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy3(void) { + uint8_t _; + /* dummy 3 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_getNonce(void) { + uint8_t _; + debug_printf("nonce : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(nonce)); + + /* get nonce */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(nonce[7]); + receiveOrNextCmd(&_); + mc_respond(nonce[6]); + receiveOrNextCmd(&_); + mc_respond(nonce[5]); + receiveOrNextCmd(&_); + mc_respond(nonce[4]); + receiveOrNextCmd(&_); + mc_respond(nonce[3]); + receiveOrNextCmd(&_); + mc_respond(nonce[2]); + receiveOrNextCmd(&_); + mc_respond(nonce[1]); + receiveOrNextCmd(&_); + mc_respond(nonce[0]); + receiveOrNextCmd(&_); + mc_respond(XOR8(nonce)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy5(void) { + uint8_t _; + /* dummy 5 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_mechaChallenge3(void) { + uint8_t _; + /* MechaChallenge3 */ + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[7]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[6]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[5]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[4]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[3]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[2]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[1]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge3[0]); + /* TODO: checksum below */ + mc_respond(0xFF); + receiveOrNextCmd(&_); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + + debug_printf("MechaChallenge3 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(MechaChallenge3)); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_mechaChallenge2(void) { + uint8_t _ = 0U; + /* MechaChallenge2 */ + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[7]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[6]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[5]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[4]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[3]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[2]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[1]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge2[0]); + /* TODO: checksum below */ + mc_respond(0xFF); + receiveOrNextCmd(&_); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + + debug_printf("MechaChallenge2 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(MechaChallenge2)); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy8(void) { + uint8_t _ = 0U; + /* dummy 8 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy9(void) { + uint8_t _ = 0U; + /* dummy 9 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummyA(void) { + uint8_t _ = 0U; + /* dummy A */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_mechaChallenge1(void) { + uint8_t _ = 0; + /* MechaChallenge1 */ + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[7]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[6]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[5]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[4]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[3]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[2]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[1]); + mc_respond(0xFF); + receiveOrNextCmd(&MechaChallenge1[0]); + /* TODO: checksum below */ + mc_respond(0xFF); + receiveOrNextCmd(&_); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + + debug_printf("MechaChallenge1 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(MechaChallenge1)); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummyC(void) { + uint8_t _ = 0; + /* dummy C */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummyD(void) { + uint8_t _ = 0; + /* dummy D */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummyE(void) { + uint8_t _ = 0; + /* dummy E */ + generateResponse(); + debug_printf("CardResponse1 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(CardResponse1)); + debug_printf("CardResponse2 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(CardResponse2)); + debug_printf("CardResponse3 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(CardResponse3)); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_cardResponse1(void) { + uint8_t _ = 0; + /* CardResponse1 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[7]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[6]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[5]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[4]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[3]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[2]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[1]); + receiveOrNextCmd(&_); + mc_respond(CardResponse1[0]); + receiveOrNextCmd(&_); + mc_respond(XOR8(CardResponse1)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy10(void) { + uint8_t _ = 0; + /* dummy 10 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_cardResponse2(void) { + uint8_t _ = 0; + /* CardResponse2 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[7]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[6]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[5]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[4]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[3]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[2]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[1]); + receiveOrNextCmd(&_); + mc_respond(CardResponse2[0]); + receiveOrNextCmd(&_); + mc_respond(XOR8(CardResponse2)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy12(void) { + uint8_t _ = 0; + /* dummy 12 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_cardResponse3(void) { + uint8_t _ = 0; + /* CardResponse3 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[7]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[6]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[5]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[4]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[3]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[2]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[1]); + receiveOrNextCmd(&_); + mc_respond(CardResponse3[0]); + receiveOrNextCmd(&_); + mc_respond(XOR8(CardResponse3)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_auth_dummy14(void) { + uint8_t _ = 0; + /* dummy 14 */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_sessionKeyEncr(void) { + uint8_t _ = 0; + uint8_t subcmd = 0; + /* session key encrypt */ + mc_respond(0xFF); + receiveOrNextCmd(&subcmd); + if (subcmd == 0x50 || subcmd == 0x40) { + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + } else if (subcmd == 0x51 || subcmd == 0x41) { + /* host mc_responds key to us */ + for (size_t i = 0; i < sizeof(hostkey); ++i) { + mc_respond(0xFF); + receiveOrNextCmd(&hostkey[i]); + } + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + } else if (subcmd == 0x52 || subcmd == 0x42) { + /* now we encrypt/decrypt the key */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + } else if (subcmd == 0x53 || subcmd == 0x43) { + mc_respond(0x2B); + receiveOrNextCmd(&_); + /* we mc_respond key to the host */ + for (size_t i = 0; i < sizeof(hostkey); ++i) { + mc_respond(hostkey[i]); + receiveOrNextCmd(&_); + } + mc_respond(term); + } else { + debug_printf("!! unknown subcmd %02X -> %02X\n", 0xF2, subcmd); + } +} + +inline __attribute__((always_inline)) void ps2_mc_auth(void) { + uint8_t subcmd = 0; + mc_respond(0xFF); + + receiveOrNextCmd(&subcmd); + // debug_printf("MC Auth: %02X\n", subcmd); + switch (subcmd) { + case 0x0: ps2_mc_auth_probe(); break; + case 0x1: ps2_mc_auth_getIv(); break; + case 0x2: ps2_mc_auth_getSeed(); break; + case 0x3: ps2_mc_auth_dummy3(); break; + case 0x4: ps2_mc_auth_getNonce(); break; + case 0x5: ps2_mc_auth_dummy5(); break; + case 0x6: ps2_mc_auth_mechaChallenge3(); break; + case 0x7: ps2_mc_auth_mechaChallenge2(); break; + case 0x8: ps2_mc_auth_dummy8(); break; + case 0x9: ps2_mc_auth_dummy9(); break; + case 0xA: ps2_mc_auth_dummyA(); break; + case 0xB: ps2_mc_auth_mechaChallenge1(); break; + case 0xC: ps2_mc_auth_dummyC(); break; + case 0xD: ps2_mc_auth_dummyD(); break; + case 0xE: ps2_mc_auth_dummyE(); break; + case 0xF: ps2_mc_auth_cardResponse1(); break; + case 0x10: ps2_mc_auth_dummy10(); break; + case 0x11: ps2_mc_auth_cardResponse2(); break; + case 0x12: ps2_mc_auth_dummy12(); break; + case 0x13: ps2_mc_auth_cardResponse3(); break; + case 0x14: ps2_mc_auth_dummy14(); break; + default: + // debug_printf("unknown %02X -> %02X\n", ch, subcmd); + break; + } +} diff --git a/src/ps2/card_emu/ps2_mc_auth.h b/src/ps2/card_emu/ps2_mc_auth.h new file mode 100644 index 0000000..d4cf9ac --- /dev/null +++ b/src/ps2/card_emu/ps2_mc_auth.h @@ -0,0 +1,9 @@ +#pragma once + +#include "pico/platform.h" + +extern void ps2_mc_auth(void); +extern void ps2_mc_sessionKeyEncr(void); + + +void __time_critical_func(generateIvSeedNonce)(void); diff --git a/src/ps2/card_emu/ps2_mc_commands.c b/src/ps2/card_emu/ps2_mc_commands.c new file mode 100644 index 0000000..6645333 --- /dev/null +++ b/src/ps2/card_emu/ps2_mc_commands.c @@ -0,0 +1,366 @@ +#include "ps2_mc_commands.h" + +#include +#include + +#include "hardware/dma.h" +#include "ps2_cardman.h" +#include "ps2_dirty.h" +#include "ps2_mc_internal.h" +#include "ps2_pio_qspi.h" + +uint32_t read_sector, write_sector, erase_sector; +struct { + uint32_t prefix; + uint8_t buf[528]; +} readtmp; +uint8_t writetmp[528]; +int is_write, is_dma_read; +uint32_t readptr, writeptr; +uint8_t *eccptr; + +inline __attribute__((always_inline)) void ps2_mc_cmd_0x11(void) { + uint8_t _ = 0U; + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_0x12(void) { + uint8_t _ = 0U; + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_setEraseAddress(void) { + uint8_t _ = 0; + /* set address for erase */ + union { + uint8_t a[4]; + uint32_t addr; + } raw; + uint8_t ck; + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[0]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[1]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[2]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[3]); + mc_respond(0xFF); + receiveOrNextCmd(&ck); + mc_respond(0x2B); + receiveOrNextCmd(&_); + (void)ck; // TODO: validate checksum + erase_sector = raw.addr; + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_setWriteAddress(void) { + uint8_t _ = 0; + /* set address for write */ + union { + uint8_t a[4]; + uint32_t addr; + } raw; + uint8_t ck; + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[0]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[1]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[2]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[3]); + mc_respond(0xFF); + receiveOrNextCmd(&ck); + mc_respond(0x2B); + receiveOrNextCmd(&_); + (void)ck; // TODO: validate checksum + write_sector = raw.addr; + is_write = 1; + writeptr = 0; + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_setReadAddress(void) { + uint8_t _ = 0U; + /* set address for read */ + union { + uint8_t a[4]; + uint32_t addr; + } raw; + uint8_t ck; + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[0]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[1]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[2]); + mc_respond(0xFF); + receiveOrNextCmd(&raw.a[3]); + mc_respond(0xFF); + receiveOrNextCmd(&ck); + mc_respond(0x2B); + receiveOrNextCmd(&_); + (void)ck; // TODO: validate checksum + read_sector = raw.addr; + if (read_sector * 512 + 512 <= ps2_cardman_get_card_size()) { + ps2_dirty_lockout_renew(); + /* the spinlock will be unlocked by the DMA irq once all data is tx'd */ + ps2_dirty_lock(); + read_mc(read_sector * 512, &readtmp, 512 + 4); + // dma_channel_wait_for_finish_blocking(0); + // dma_channel_wait_for_finish_blocking(1); + } + readptr = 0; + + eccptr = &readtmp.buf[512]; + memset(eccptr, 0, 16); + + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_getSpecs(void) { + uint8_t _ = 0; + /* GET_SPECS ? */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + uint32_t sector_count = (flash_mode) ? PS2_CARD_SIZE_1M / 512 : (uint32_t)(ps2_cardman_get_card_size() / 512); + + uint8_t specs[] = {0x00, 0x02, ERASE_SECTORS, 0x00, 0x00, 0x40, 0x00, 0x00}; + specs[4] = (uint8_t)(sector_count & 0xFF); + specs[5] = (uint8_t)((sector_count >> 8) & 0xFF); + specs[6] = (uint8_t)((sector_count >> 16) & 0xFF); + specs[7] = (uint8_t)((sector_count >> 24) & 0xFF); + + mc_respond(specs[0]); + receiveOrNextCmd(&_); + mc_respond(specs[1]); + receiveOrNextCmd(&_); + mc_respond(specs[2]); + receiveOrNextCmd(&_); + mc_respond(specs[3]); + receiveOrNextCmd(&_); + mc_respond(specs[4]); + receiveOrNextCmd(&_); + mc_respond(specs[5]); + receiveOrNextCmd(&_); + mc_respond(specs[6]); + receiveOrNextCmd(&_); + mc_respond(specs[7]); + receiveOrNextCmd(&_); + mc_respond(XOR8(specs)); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_setTerminator(void) { + uint8_t _ = 0U; + /* SET_TERMINATOR */ + mc_respond(0xFF); + receiveOrNextCmd(&term); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_getTerminator(void) { + uint8_t _ = 0U; + /* GET_TERMINATOR */ + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_writeData(void) { + uint8_t ck2 = 0U; + uint8_t _ = 0U; + /* write data */ + uint8_t sz; + mc_respond(0xFF); + receiveOrNextCmd(&sz); + mc_respond(0xFF); + +#ifdef DEBUG_MC_PROTOCOL + debug_printf("> %02X %02X\n", ch, sz); +#endif + + uint8_t ck = 0; + uint8_t b; + + for (int i = 0; i < sz; ++i) { + receiveOrNextCmd(&b); + if (writeptr < sizeof(writetmp)) { + writetmp[writeptr] = b; + ++writeptr; + } + ck ^= b; + mc_respond(0xFF); + } + // this should be checksum? + receiveOrNextCmd(&ck2); + (void)ck2; // TODO: validate checksum + + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_readData(void) { + uint8_t _ = 0U; + /* read data */ + uint8_t sz; + mc_respond(0xFF); + receiveOrNextCmd(&sz); + mc_respond(0x2B); + receiveOrNextCmd(&_); + +#ifdef DEBUG_MC_PROTOCOL + debug_printf("> %02X %02X\n", ch, sz); +#endif + + uint8_t ck = 0; + uint8_t b = 0xFF; + + for (int i = 0; i < sz; ++i) { + if (readptr == sizeof(readtmp.buf)) { + /* a game may read more than one 528-byte sector in a sequence of read ops, e.g. re4 */ + ++read_sector; + if (read_sector * 512 + 512 <= ps2_cardman_get_card_size()) { + ps2_dirty_lockout_renew(); + /* the spinlock will be unlocked by the DMA irq once all data is tx'd */ + ps2_dirty_lock(); + read_mc(read_sector * 512, &readtmp, 512 + 4); + // TODO: remove this if safe + // must make sure the dma completes for first byte before we start reading below + dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_RX_CHAN); + dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_TX_CHAN); + } + readptr = 0; + + eccptr = &readtmp.buf[512]; + memset(eccptr, 0, 16); + } + + if (readptr < sizeof(readtmp.buf)) { + b = readtmp.buf[readptr]; + mc_respond(b); + + if (readptr <= 512) { + uint8_t c = EccTable[b]; + eccptr[0] ^= c; + if (c & 0x80) { + eccptr[1] ^= ~(readptr & 0x7F); + eccptr[2] ^= (readptr & 0x7F); + } + + ++readptr; + + if ((readptr & 0x7F) == 0) { + eccptr[0] = ~eccptr[0]; + eccptr[0] &= 0x77; + + eccptr[1] = ~eccptr[1]; + eccptr[1] &= 0x7f; + + eccptr[2] = ~eccptr[2]; + eccptr[2] &= 0x7f; + + eccptr += 3; + } + } else { + ++readptr; + } + } else + mc_respond(b); + ck ^= b; + receiveOrNextCmd(&_); + } + + mc_respond(ck); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_commitData(void) { + uint8_t _ = 0; + /* commit for read/write? */ + if (is_write) { + is_write = 0; + if (write_sector * 512 + 512 <= ps2_cardman_get_card_size()) { + ps2_dirty_lockout_renew(); + ps2_dirty_lock(); + write_mc(write_sector * 512, writetmp, 512); + ps2_dirty_mark(write_sector); + ps2_dirty_unlock(); +#ifdef DEBUG_MC_PROTOCOL + debug_printf("WR 0x%08X : %02X %02X .. %08X %08X %08X\n", write_sector * 512, writetmp[0], writetmp[1], *(uint32_t *)&writetmp[512], + *(uint32_t *)&writetmp[516], *(uint32_t *)&writetmp[520]); +#endif + } + } else { +#ifdef DEBUG_MC_PROTOCOL + debug_printf("RD 0x%08X : %02X %02X .. %08X %08X %08X\n", read_sector * 512, readtmp.buf[0], readtmp.buf[1], *(uint32_t *)&readtmp.buf[512], + *(uint32_t *)&readtmp.buf[516], *(uint32_t *)&readtmp.buf[520]); +#endif + } + + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_erase(void) { + uint8_t _ = 0U; + /* do erase */ + if (erase_sector * 512 + 512 * ERASE_SECTORS <= ps2_cardman_get_card_size()) { + memset(readtmp.buf, 0xFF, 512); + ps2_dirty_lockout_renew(); + ps2_dirty_lock(); + for (int i = 0; i < ERASE_SECTORS; ++i) { + write_mc((erase_sector + i) * 512, readtmp.buf, 512); + ps2_dirty_mark(erase_sector + i); + } + ps2_dirty_unlock(); +#ifdef DEBUG_MC_PROTOCOL + debug_printf("ER 0x%08X\n", erase_sector * 512); +#endif + } + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_0xBF(void) { + uint8_t _ = 0U; + mc_respond(0xFF); + receiveOrNextCmd(&_); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_0xF3(void) { + uint8_t _ = 0U; + mc_respond(0xFF); + receiveOrNextCmd(&_); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} + +inline __attribute__((always_inline)) void ps2_mc_cmd_keySelect( + void) { // TODO: it fails to get detected at all when ps2_magicgate==0, check if it's intentional + uint8_t _ = 0U; + /* SIO_MEMCARD_KEY_SELECT */ + mc_respond(0xFF); + receiveOrNextCmd(&_); + mc_respond(0x2B); + receiveOrNextCmd(&_); + mc_respond(term); +} diff --git a/src/ps2/card_emu/ps2_mc_commands.h b/src/ps2/card_emu/ps2_mc_commands.h new file mode 100644 index 0000000..565704b --- /dev/null +++ b/src/ps2/card_emu/ps2_mc_commands.h @@ -0,0 +1,40 @@ +#pragma once + +#define PS2_SIO2_CMD_IDENTIFIER 0x81 + +#define PS2_SIO2_CMD_0x11 0x11 +#define PS2_SIO2_CMD_0x12 0x12 +#define PS2_SIO2_CMD_SET_ERASE_ADDRESS 0x21 +#define PS2_SIO2_CMD_SET_WRITE_ADDRESS 0x22 +#define PS2_SIO2_CMD_SET_READ_ADDRESS 0x23 +#define PS2_SIO2_CMD_GET_SPECS 0x26 +#define PS2_SIO2_CMD_SET_TERMINATOR 0x27 +#define PS2_SIO2_CMD_GET_TERMINATOR 0x28 +#define PS2_SIO2_CMD_WRITE_DATA 0x42 +#define PS2_SIO2_CMD_READ_DATA 0x43 +#define PS2_SIO2_CMD_COMMIT_DATA 0x81 +#define PS2_SIO2_CMD_ERASE 0x82 +#define PS2_SIO2_CMD_BF 0xBF +#define PS2_SIO2_CMD_F3 0xF3 +#define PS2_SIO2_CMD_KEY_SELECT 0xF7 +#define PS2_SIO2_CMD_AUTH 0xF0 +#define PS2_SIO2_CMD_SESSION_KEY_0 0xF1 +#define PS2_SIO2_CMD_SESSION_KEY_1 0xF2 + + +extern void ps2_mc_cmd_0x11(void); +extern void ps2_mc_cmd_0x12(void); +extern void ps2_mc_cmd_setEraseAddress(void); +extern void ps2_mc_cmd_setWriteAddress(void); +extern void ps2_mc_cmd_setReadAddress(void); +extern void ps2_mc_cmd_getSpecs(void); +extern void ps2_mc_cmd_setTerminator(void); +extern void ps2_mc_cmd_getTerminator(void); +extern void ps2_mc_cmd_writeData(void); +extern void ps2_mc_cmd_readData(void); +extern void ps2_mc_cmd_commitData(void); +extern void ps2_mc_cmd_erase(void); +extern void ps2_mc_cmd_0xBF(void); +extern void ps2_mc_cmd_0xF3(void); +extern void ps2_mc_cmd_keySelect(void); + diff --git a/src/ps2/card_emu/ps2_mc_internal.h b/src/ps2/card_emu/ps2_mc_internal.h new file mode 100644 index 0000000..9ddfd02 --- /dev/null +++ b/src/ps2/card_emu/ps2_mc_internal.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../ps2_exploit.h" +#include "../ps2_dirty.h" +#include "../ps2_psram.h" + + +#include +#include + + +#define ERASE_SECTORS 16 +#define CARD_SIZE (8 * 1024 * 1024) + +#define XOR8(a) (a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5] ^ a[6] ^ a[7]) +#define ARG8(a) a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7] + +enum { RECEIVE_RESET, RECEIVE_EXIT, RECEIVE_OK }; + +extern uint8_t term; +extern uint32_t read_sector, write_sector, erase_sector; +extern uint8_t writetmp[528]; +extern int is_write, is_dma_read; +extern uint32_t readptr, writeptr; +extern uint8_t *eccptr; +extern bool flash_mode; + +extern uint8_t EccTable[]; + +extern uint8_t receive(uint8_t *cmd); +extern uint8_t receiveFirst(uint8_t *cmd); +extern void __time_critical_func(mc_respond)(uint8_t ch); +extern void __time_critical_func(read_mc)(uint32_t addr, void *buf, size_t sz); +extern void __time_critical_func(write_mc)(uint32_t addr, void *buf, size_t sz); + +#define receiveOrNextCmd(cmd) \ + if (receive(cmd) == RECEIVE_RESET) \ + return diff --git a/src/ps2/ps2_mc_spi.pio b/src/ps2/card_emu/ps2_mc_spi.pio similarity index 71% rename from src/ps2/ps2_mc_spi.pio rename to src/ps2/card_emu/ps2_mc_spi.pio index 386c58a..0ed0716 100644 --- a/src/ps2/ps2_mc_spi.pio +++ b/src/ps2/card_emu/ps2_mc_spi.pio @@ -5,6 +5,7 @@ .define PUBLIC PIN_PSX_CLK 18 .define PUBLIC PIN_PSX_CMD 19 .define PUBLIC PIN_PSX_DAT 20 +.define PUBLIC PIN_PSX_SPD_SEL 10 .program cmd_reader wait 0 gpio PIN_PSX_SEL ; wait for SEL @@ -18,6 +19,7 @@ ; wait for SEL wait 0 gpio PIN_PSX_SEL .wrap_target +wait_pull: ; wait for the arm core to give us a byte to send pull block @@ -25,6 +27,18 @@ set pins, 0 [31] set pins, 1 + jmp pin send_fast + + ; Sending slow + set x, 7 +sendbit: + ; wait for falling clock edge + wait 1 gpio PIN_PSX_CLK + wait 0 gpio PIN_PSX_CLK + out pins 1 [2] + jmp x-- sendbit + jmp wait_pull +send_fast: ; we need to output bits one clock early due to rp2040 input/output delay ; so output the first bit here and the rest after the clock goes low out pins 1 @@ -38,33 +52,17 @@ out pins 1 [9] .wrap -.program dat_writer_slow - ; wait for SEL - wait 0 gpio PIN_PSX_SEL -.wrap_target - ; wait for the arm core to give us a byte to send - pull block - - ; pulse ACK - set pins, 0 [31] - set pins, 1 - - set x, 7 -sendbit: - ; wait for falling clock edge - wait 1 gpio PIN_PSX_CLK - wait 0 gpio PIN_PSX_CLK - out pins 1 [2] - jmp x-- sendbit -.wrap - .program clock_probe - wait 0 gpio PIN_PSX_SEL - wait 0 gpio PIN_PSX_CLK - nop [2] ; wait 24ns - this will get us into high clock phase for 25mhz, stay in low clock phase for 8mhz and under - in pins 1 + wait 0 gpio PIN_PSX_SEL + wait 0 gpio PIN_PSX_CLK [2] ; wait 24ns - this will get us into high clock phase for 25mhz, stay in low clock phase for 8mhz and under + jmp PIN setpin + set pins 0 +.wrap_target infloop: jmp infloop +setpin: + set pins 1 +.wrap % c-sdk { @@ -90,6 +88,7 @@ static inline void dat_writer_program_init(PIO pio, uint sm, uint offset) { sm_config_set_out_pins(&c, PIN_PSX_DAT, 1); sm_config_set_set_pins(&c, PIN_PSX_ACK, 1); + sm_config_set_jmp_pin(&c, PIN_PSX_SPD_SEL); /* configure ACK pin for output */ pio_sm_set_pins_with_mask(pio, sm, 1 << PIN_PSX_ACK, 1 << PIN_PSX_ACK); @@ -104,6 +103,7 @@ static inline void dat_writer_program_init(PIO pio, uint sm, uint offset) { /* SEL and CLK used as "wait" inputs only */ pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); +// pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SPD_SEL, 1, false); /* shift OSR to right, autopull every 8 bits */ sm_config_set_out_shift(&c, true, true, 8); @@ -112,43 +112,24 @@ static inline void dat_writer_program_init(PIO pio, uint sm, uint offset) { pio_sm_init(pio, sm, offset, &c); } -static inline void dat_writer_slow_program_init(PIO pio, uint sm, uint offset) { - pio_sm_config c = dat_writer_slow_program_get_default_config(offset); - - sm_config_set_out_pins(&c, PIN_PSX_DAT, 1); - sm_config_set_set_pins(&c, PIN_PSX_ACK, 1); - - /* configure ACK pin for output */ - pio_sm_set_pins_with_mask(pio, sm, 1 << PIN_PSX_ACK, 1 << PIN_PSX_ACK); - pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_ACK, 1, true); - pio_gpio_init(pio, PIN_PSX_ACK); - - /* configure DAT pin for output */ - pio_sm_set_pins_with_mask(pio, sm, 1 << PIN_PSX_DAT, 1 << PIN_PSX_DAT); - pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_DAT, 1, true); - pio_gpio_init(pio, PIN_PSX_DAT); - - /* SEL and CLK used as "wait" inputs only */ - pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); - pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); - - /* shift OSR to right, autopull every 8 bits */ - sm_config_set_out_shift(&c, true, true, 8); - sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); - - pio_sm_init(pio, sm, offset, &c); -} static inline void clock_probe_program_init(PIO pio, uint sm, uint offset) { pio_sm_config c = clock_probe_program_get_default_config(offset); + pio_gpio_init(pio, PIN_PSX_SPD_SEL); - sm_config_set_in_pins(&c, PIN_PSX_CLK); pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SPD_SEL, 1, true); + /* shift ISR to right, autopush every bit */ + sm_config_set_in_pins(&c, PIN_PSX_CLK); sm_config_set_in_shift(&c, true, true, 1); sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + sm_config_set_jmp_pin(&c, PIN_PSX_CLK); + sm_config_set_set_pins(&c, PIN_PSX_SPD_SEL, 1); + + pio_sm_set_pins_with_mask(pio, sm, 0, 1 << PIN_PSX_SPD_SEL); sm_config_set_clkdiv_int_frac(&c, 2, 0); diff --git a/src/ps2/card_emu/ps2_memory_card.c b/src/ps2/card_emu/ps2_memory_card.c new file mode 100644 index 0000000..f794e3d --- /dev/null +++ b/src/ps2/card_emu/ps2_memory_card.c @@ -0,0 +1,360 @@ +#include "../ps2_dirty.h" +#include "../ps2_exploit.h" +#include "../ps2_psram.h" +#include "debug.h" +#include "hardware/gpio.h" +#include "hardware/timer.h" +#include "keystore.h" +#include "pico/platform.h" +#include "ps2_mc_auth.h" +#include "ps2_mc_commands.h" +#include "ps2_mc_internal.h" +#include "ps2_mc_spi.pio.h" + +#include +#include +#include + +#include "ps2_sd2psxman_commands.h" + +// #define DEBUG_MC_PROTOCOL + +uint64_t us_startup; + +volatile int reset; + +bool flash_mode = false; + +typedef struct { + uint32_t offset; + uint32_t sm; +} pio_t; + +pio_t cmd_reader, dat_writer, clock_probe; +uint8_t term = 0xFF; + +static volatile int mc_exit_request, mc_exit_response, mc_enter_request, mc_enter_response; + +inline void __time_critical_func(read_mc)(uint32_t addr, void *buf, size_t sz) { + if (flash_mode) { + ps2_exploit_read(addr, buf, sz); + ps2_dirty_unlock(); + } else { + psram_read_dma(addr, buf, sz); + } +} + +inline void __time_critical_func(write_mc)(uint32_t addr, void *buf, size_t sz) { + if (!flash_mode) { + psram_write(addr, buf, sz); + } else { + ps2_dirty_unlock(); + } +} + +static inline void __time_critical_func(RAM_pio_sm_drain_tx_fifo)(PIO pio, uint sm) { + uint instr = (pio->sm[sm].shiftctrl & PIO_SM0_SHIFTCTRL_AUTOPULL_BITS) ? pio_encode_out(pio_null, 32) : pio_encode_pull(false, false); + while (!pio_sm_is_tx_fifo_empty(pio, sm)) { + pio_sm_exec(pio, sm, instr); + } +} + +static void __time_critical_func(reset_pio)(void) { + pio_set_sm_mask_enabled(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm) | (1 << clock_probe.sm), false); + pio_restart_sm_mask(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm) | (1 << clock_probe.sm)); + + pio_sm_exec(pio0, cmd_reader.sm, pio_encode_jmp(cmd_reader.offset)); + pio_sm_exec(pio0, dat_writer.sm, pio_encode_jmp(dat_writer.offset)); + pio_sm_exec(pio0, clock_probe.sm, pio_encode_jmp(clock_probe.offset)); + + pio_sm_clear_fifos(pio0, cmd_reader.sm); + RAM_pio_sm_drain_tx_fifo(pio0, dat_writer.sm); + pio_sm_clear_fifos(pio0, clock_probe.sm); + + pio_enable_sm_mask_in_sync(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm) | (1 << clock_probe.sm)); + + reset = 1; +} + +static void __time_critical_func(init_pio)(void) { + /* Set all pins as floating inputs */ + gpio_set_dir(PIN_PSX_ACK, 0); + gpio_set_dir(PIN_PSX_SEL, 0); + gpio_set_dir(PIN_PSX_CLK, 0); + gpio_set_dir(PIN_PSX_CMD, 0); + gpio_set_dir(PIN_PSX_DAT, 0); + gpio_set_dir(PIN_PSX_SPD_SEL, true); + gpio_disable_pulls(PIN_PSX_ACK); + gpio_disable_pulls(PIN_PSX_SEL); + gpio_disable_pulls(PIN_PSX_CLK); + gpio_disable_pulls(PIN_PSX_CMD); + gpio_disable_pulls(PIN_PSX_DAT); + gpio_disable_pulls(PIN_PSX_SPD_SEL); + + cmd_reader.offset = pio_add_program(pio0, &cmd_reader_program); + cmd_reader.sm = pio_claim_unused_sm(pio0, true); + + dat_writer.offset = pio_add_program(pio0, &dat_writer_program); + dat_writer.sm = pio_claim_unused_sm(pio0, true); + + clock_probe.offset = pio_add_program(pio0, &clock_probe_program); + clock_probe.sm = pio_claim_unused_sm(pio0, true); + + cmd_reader_program_init(pio0, cmd_reader.sm, cmd_reader.offset); + dat_writer_program_init(pio0, dat_writer.sm, dat_writer.offset); + clock_probe_program_init(pio0, clock_probe.sm, clock_probe.offset); +} + +static void __time_critical_func(card_deselected)(uint gpio, uint32_t event_mask) { + if (gpio == PIN_PSX_SEL && (event_mask & GPIO_IRQ_EDGE_RISE)) { + reset_pio(); + } +} + +inline __attribute__((always_inline)) uint8_t receive(uint8_t *cmd) { + while (pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && + pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && 1) { + if (reset) + return RECEIVE_RESET; + } + (*cmd) = (pio_sm_get(pio0, cmd_reader.sm) >> 24); + return RECEIVE_OK; +} + +inline __attribute__((always_inline)) uint8_t receiveFirst(uint8_t *cmd) { + while (pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && + pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && 1) { + if (reset) + return RECEIVE_RESET; + if (mc_exit_request) + return RECEIVE_EXIT; + } + (*cmd) = (pio_sm_get(pio0, cmd_reader.sm) >> 24); + return RECEIVE_OK; +} + +inline void __time_critical_func(mc_respond)(uint8_t ch) { + pio_sm_put_blocking(pio0, dat_writer.sm, ch); +} + +uint8_t EccTable[] = { + 0x00, 0x87, 0x96, 0x11, 0xa5, 0x22, 0x33, 0xb4, 0xb4, 0x33, 0x22, 0xa5, 0x11, 0x96, 0x87, 0x00, 0xc3, 0x44, 0x55, 0xd2, 0x66, 0xe1, 0xf0, 0x77, 0x77, 0xf0, + 0xe1, 0x66, 0xd2, 0x55, 0x44, 0xc3, 0xd2, 0x55, 0x44, 0xc3, 0x77, 0xf0, 0xe1, 0x66, 0x66, 0xe1, 0xf0, 0x77, 0xc3, 0x44, 0x55, 0xd2, 0x11, 0x96, 0x87, 0x00, + 0xb4, 0x33, 0x22, 0xa5, 0xa5, 0x22, 0x33, 0xb4, 0x00, 0x87, 0x96, 0x11, 0xe1, 0x66, 0x77, 0xf0, 0x44, 0xc3, 0xd2, 0x55, 0x55, 0xd2, 0xc3, 0x44, 0xf0, 0x77, + 0x66, 0xe1, 0x22, 0xa5, 0xb4, 0x33, 0x87, 0x00, 0x11, 0x96, 0x96, 0x11, 0x00, 0x87, 0x33, 0xb4, 0xa5, 0x22, 0x33, 0xb4, 0xa5, 0x22, 0x96, 0x11, 0x00, 0x87, + 0x87, 0x00, 0x11, 0x96, 0x22, 0xa5, 0xb4, 0x33, 0xf0, 0x77, 0x66, 0xe1, 0x55, 0xd2, 0xc3, 0x44, 0x44, 0xc3, 0xd2, 0x55, 0xe1, 0x66, 0x77, 0xf0, 0xf0, 0x77, + 0x66, 0xe1, 0x55, 0xd2, 0xc3, 0x44, 0x44, 0xc3, 0xd2, 0x55, 0xe1, 0x66, 0x77, 0xf0, 0x33, 0xb4, 0xa5, 0x22, 0x96, 0x11, 0x00, 0x87, 0x87, 0x00, 0x11, 0x96, + 0x22, 0xa5, 0xb4, 0x33, 0x22, 0xa5, 0xb4, 0x33, 0x87, 0x00, 0x11, 0x96, 0x96, 0x11, 0x00, 0x87, 0x33, 0xb4, 0xa5, 0x22, 0xe1, 0x66, 0x77, 0xf0, 0x44, 0xc3, + 0xd2, 0x55, 0x55, 0xd2, 0xc3, 0x44, 0xf0, 0x77, 0x66, 0xe1, 0x11, 0x96, 0x87, 0x00, 0xb4, 0x33, 0x22, 0xa5, 0xa5, 0x22, 0x33, 0xb4, 0x00, 0x87, 0x96, 0x11, + 0xd2, 0x55, 0x44, 0xc3, 0x77, 0xf0, 0xe1, 0x66, 0x66, 0xe1, 0xf0, 0x77, 0xc3, 0x44, 0x55, 0xd2, 0xc3, 0x44, 0x55, 0xd2, 0x66, 0xe1, 0xf0, 0x77, 0x77, 0xf0, + 0xe1, 0x66, 0xd2, 0x55, 0x44, 0xc3, 0x00, 0x87, 0x96, 0x11, 0xa5, 0x22, 0x33, 0xb4, 0xb4, 0x33, 0x22, 0xa5, 0x11, 0x96, 0x87, 0x00}; + +void calcECC(uint8_t *ecc, const uint8_t *data) { + int i, c; + + ecc[0] = ecc[1] = ecc[2] = 0; + + for (i = 0; i < 0x80; i++) { + c = EccTable[data[i]]; + + ecc[0] ^= c; + if (c & 0x80) { + ecc[1] ^= ~i; + ecc[2] ^= i; + } + } + ecc[0] = ~ecc[0]; + ecc[0] &= 0x77; + + ecc[1] = ~ecc[1]; + ecc[1] &= 0x7f; + + ecc[2] = ~ecc[2]; + ecc[2] &= 0x7f; + + return; +} + +static void __time_critical_func(mc_main_loop)(void) { + while (1) { + uint8_t cmd = 0; + + while (!reset && !reset && !reset && !reset && !reset) { + if (mc_exit_request) { + mc_exit_response = 1; + return; + } + } + reset = 0; + + uint8_t received = receiveFirst(&cmd); + + if (received == RECEIVE_EXIT) { + mc_exit_response = 1; + printf("Received EXIT\n"); + break; + } + if (received == RECEIVE_RESET) + continue; + + if (cmd == PS2_SIO2_CMD_IDENTIFIER) { + /* resp to 0x81 */ + mc_respond(0xFF); + + /* sub cmd */ + receiveOrNextCmd(&cmd); + + switch (cmd) { + case PS2_SIO2_CMD_0x11: ps2_mc_cmd_0x11(); break; + case PS2_SIO2_CMD_0x12: ps2_mc_cmd_0x12(); break; + case PS2_SIO2_CMD_SET_ERASE_ADDRESS: ps2_mc_cmd_setEraseAddress(); break; + case PS2_SIO2_CMD_SET_WRITE_ADDRESS: ps2_mc_cmd_setWriteAddress(); break; + case PS2_SIO2_CMD_SET_READ_ADDRESS: ps2_mc_cmd_setReadAddress(); break; + case PS2_SIO2_CMD_GET_SPECS: ps2_mc_cmd_getSpecs(); break; + case PS2_SIO2_CMD_SET_TERMINATOR: ps2_mc_cmd_setTerminator(); break; + case PS2_SIO2_CMD_GET_TERMINATOR: ps2_mc_cmd_getTerminator(); break; + case PS2_SIO2_CMD_WRITE_DATA: ps2_mc_cmd_writeData(); break; + case PS2_SIO2_CMD_READ_DATA: ps2_mc_cmd_readData(); break; + case PS2_SIO2_CMD_COMMIT_DATA: ps2_mc_cmd_commitData(); break; + case PS2_SIO2_CMD_ERASE: ps2_mc_cmd_erase(); break; + case PS2_SIO2_CMD_BF: ps2_mc_cmd_0xBF(); break; + case PS2_SIO2_CMD_F3: ps2_mc_cmd_0xF3(); break; + case PS2_SIO2_CMD_KEY_SELECT: ps2_mc_cmd_keySelect(); break; + case PS2_SIO2_CMD_AUTH: + if (ps2_magicgate) + ps2_mc_auth(); + break; + case PS2_SIO2_CMD_SESSION_KEY_0: + case PS2_SIO2_CMD_SESSION_KEY_1: ps2_mc_sessionKeyEncr(); break; + default: debug_printf("Unknown Subcommand: %02x\n", cmd); break; + } + } else if (cmd == PS2_SD2PSXMAN_CMD_IDENTIFIER) { + /* resp to 0x8B */ + mc_respond(0xAA); + + /* sub cmd */ + receiveOrNextCmd(&cmd); + + switch (cmd) + { + case SD2PSXMAN_PING: ps2_sd2psxman_cmds_ping(); break; + case SD2PSXMAN_GET_STATUS: ps2_sd2psxman_cmds_get_status(); break; + case SD2PSXMAN_GET_CARD: ps2_sd2psxman_cmds_get_card(); break; + case SD2PSXMAN_SET_CARD: ps2_sd2psxman_cmds_set_card(); break; + case SD2PSXMAN_GET_CHANNEL: ps2_sd2psxman_cmds_get_channel(); break; + case SD2PSXMAN_SET_CHANNEL: ps2_sd2psxman_cmds_set_channel(); break; + case SD2PSXMAN_GET_GAMEID: ps2_sd2psxman_cmds_get_gameid(); break; + case SD2PSXMAN_SET_GAMEID: ps2_sd2psxman_cmds_set_gameid(); break; + case SD2PSXMAN_UNMOUNT_BOOTCARD: ps2_sd2psxman_cmds_unmount_bootcard(); break; + default: debug_printf("Unknown Subcommand: %02x\n", cmd); break; + } + } else { + // not for us + continue; + } + } +} + +static void __no_inline_not_in_flash_func(mc_main)(void) { + while (1) { + while (!mc_enter_request) {} + mc_enter_response = 1; + + reset_pio(); + mc_main_loop(); + } +} + +static gpio_irq_callback_t callbacks[NUM_CORES]; + +static void __time_critical_func(RAM_gpio_acknowledge_irq)(uint gpio, uint32_t events) { + check_gpio_param(gpio); + iobank0_hw->intr[gpio / 8] = events << (4 * (gpio % 8)); +} + +static void __time_critical_func(RAM_gpio_default_irq_handler)(void) { + uint core = get_core_num(); + gpio_irq_callback_t callback = callbacks[core]; + io_irq_ctrl_hw_t *irq_ctrl_base = core ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl; + for (uint gpio = 0; gpio < NUM_BANK0_GPIOS; gpio += 8) { + uint32_t events8 = irq_ctrl_base->ints[gpio >> 3u]; + // note we assume events8 is 0 for non-existent GPIO + for (uint i = gpio; events8 && i < gpio + 8; i++) { + uint32_t events = events8 & 0xfu; + if (events) { + RAM_gpio_acknowledge_irq(i, events); + if (callback) { + callback(i, events); + } + } + events8 >>= 4; + } + } +} + +static void my_gpio_set_irq_callback(gpio_irq_callback_t callback) { + uint core = get_core_num(); + if (callbacks[core]) { + if (!callback) { + irq_remove_handler(IO_IRQ_BANK0, RAM_gpio_default_irq_handler); + } + callbacks[core] = callback; + } else if (callback) { + callbacks[core] = callback; + irq_add_shared_handler(IO_IRQ_BANK0, RAM_gpio_default_irq_handler, GPIO_IRQ_CALLBACK_ORDER_PRIORITY); + } +} + +static void my_gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback) { + gpio_set_irq_enabled(gpio, events, enabled); + my_gpio_set_irq_callback(callback); + if (enabled) + irq_set_enabled(IO_IRQ_BANK0, true); +} + +void ps2_memory_card_main(void) { + init_pio(); + generateIvSeedNonce(); + + us_startup = time_us_64(); + debug_printf("Secondary core!\n"); + + my_gpio_set_irq_enabled_with_callback(PIN_PSX_SEL, GPIO_IRQ_EDGE_RISE, 1, card_deselected); + + gpio_set_slew_rate(PIN_PSX_DAT, GPIO_SLEW_RATE_FAST); + gpio_set_drive_strength(PIN_PSX_DAT, GPIO_DRIVE_STRENGTH_12MA); + mc_main(); +} + +static int memcard_running; + +void ps2_memory_card_exit(void) { + if (!memcard_running) + return; + + mc_exit_request = 1; + while (!mc_exit_response) {} + mc_exit_request = mc_exit_response = 0; + memcard_running = 0; +} + +void ps2_memory_card_enter(void) { + if (flash_mode) { + ps2_memory_card_exit(); + } else if (memcard_running) + return; + + mc_enter_request = 1; + while (!mc_enter_response) {} + mc_enter_request = mc_enter_response = 0; + memcard_running = 1; + flash_mode = false; +} + +void ps2_memory_card_enter_flash(void) { + mc_enter_request = 1; + while (!mc_enter_response) {} + mc_enter_request = mc_enter_response = 0; + memcard_running = 1; + flash_mode = true; +} \ No newline at end of file diff --git a/src/ps2/ps2_memory_card.h b/src/ps2/card_emu/ps2_memory_card.h similarity index 68% rename from src/ps2/ps2_memory_card.h rename to src/ps2/card_emu/ps2_memory_card.h index 688f35e..4c857c9 100644 --- a/src/ps2/ps2_memory_card.h +++ b/src/ps2/card_emu/ps2_memory_card.h @@ -1,8 +1,6 @@ #pragma once -#include - void ps2_memory_card_main(void); void ps2_memory_card_enter(void); void ps2_memory_card_enter_flash(void); -void ps2_memory_card_exit(void); +void ps2_memory_card_exit(void); \ No newline at end of file diff --git a/src/ps2/card_emu/ps2_sd2psxman.c b/src/ps2/card_emu/ps2_sd2psxman.c new file mode 100644 index 0000000..8d81ccd --- /dev/null +++ b/src/ps2/card_emu/ps2_sd2psxman.c @@ -0,0 +1,86 @@ +#include "ps2/card_emu/ps2_sd2psxman.h" + +#include + +#include "debug.h" +#include "gui.h" +#include "ps2/card_emu/ps2_memory_card.h" +#include "ps2/card_emu/ps2_sd2psxman_commands.h" +#include "ps2/ps2_cardman.h" + +#include "pico/platform.h" + +volatile uint8_t sd2psxman_cmd; +volatile uint8_t sd2psxman_mode; +volatile uint16_t sd2psxman_cnum; +char sd2psxman_gameid[251] = {0x00}; + +void ps2_sd2psxman_task(void) { + if (sd2psxman_cmd != 0) { + uint16_t prev_card = ps2_cardman_get_idx(); + uint8_t prev_chan = ps2_cardman_get_channel(); + ps2_cardman_state_t prev_state = ps2_cardman_get_state(); + + switch (sd2psxman_cmd) { + case SD2PSXMAN_SET_CARD: + if (sd2psxman_mode == SD2PSXMAN_MODE_NUM) { + ps2_cardman_set_idx(sd2psxman_cnum); + debug_printf("set num idx\n"); + } else if (sd2psxman_mode == SD2PSXMAN_MODE_NEXT) { + ps2_cardman_next_idx(); + debug_printf("set next idx\n"); + } else if (sd2psxman_mode == SD2PSXMAN_MODE_PREV) { + ps2_cardman_prev_idx(); + debug_printf("set prev idx\n"); + } + break; + + case SD2PSXMAN_SET_CHANNEL: + if (sd2psxman_mode == SD2PSXMAN_MODE_NUM) { + ps2_cardman_set_channel(sd2psxman_cnum); + debug_printf("set num channel\n"); + } else if (sd2psxman_mode == SD2PSXMAN_MODE_NEXT) { + ps2_cardman_next_channel(); + debug_printf("set next channel\n"); + } else if (sd2psxman_mode == SD2PSXMAN_MODE_PREV) { + ps2_cardman_prev_channel(); + debug_printf("set prev channel\n"); + } + break; + + case SD2PSXMAN_SET_GAMEID: + ps2_cardman_set_gameid(sd2psxman_gameid); + debug_printf("set next channel\n"); + break; + case SD2PSXMAN_UNMOUNT_BOOTCARD: + if (ps2_cardman_get_idx() == 0) { + ps2_cardman_next_idx(); + } + break; + + default: break; + } + + if (prev_card != ps2_cardman_get_idx() || prev_chan != ps2_cardman_get_channel() || (prev_state != ps2_cardman_get_state()) || + (SD2PSXMAN_SET_GAMEID == sd2psxman_cmd)) { + // close old card + ps2_memory_card_exit(); + ps2_cardman_close(); + + // open new card + gui_do_ps2_card_switch(); + // ps2_memory_card_enter(); + gui_request_refresh(); + } + + sd2psxman_cmd = 0; + } +} + +void __time_critical_func(ps2_sd2psxman_set_gameid)(const char* const game_id) { + snprintf(sd2psxman_gameid, sizeof(sd2psxman_gameid), "%s", game_id); +} + +const char* ps2_sd2psxman_get_gameid(void) { + return sd2psxman_gameid; +} \ No newline at end of file diff --git a/src/ps2/card_emu/ps2_sd2psxman.h b/src/ps2/card_emu/ps2_sd2psxman.h new file mode 100644 index 0000000..a960097 --- /dev/null +++ b/src/ps2/card_emu/ps2_sd2psxman.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +extern volatile uint8_t sd2psxman_cmd; +extern volatile uint8_t sd2psxman_mode; +extern volatile uint16_t sd2psxman_cnum; +extern char sd2psxman_gameid[251]; + +void ps2_sd2psxman_task(void); +void ps2_sd2psxman_set_gameid(const char* const game_id); +const char* ps2_sd2psxman_get_gameid(void); \ No newline at end of file diff --git a/src/ps2/card_emu/ps2_sd2psxman_commands.c b/src/ps2/card_emu/ps2_sd2psxman_commands.c new file mode 100644 index 0000000..e739ee5 --- /dev/null +++ b/src/ps2/card_emu/ps2_sd2psxman_commands.c @@ -0,0 +1,142 @@ +#include + +#include "ps2_cardman.h" +#include "ps2_mc_internal.h" + +#include "ps2_sd2psxman.h" +#include "ps2_sd2psxman_commands.h" + +#include "game_names/game_names.h" + +#include "debug.h" + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_ping(void) +{ + uint8_t cmd; + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(0x1); receiveOrNextCmd(&cmd); //protocol version + mc_respond(0x1); receiveOrNextCmd(&cmd); //product ID + mc_respond(0x1); receiveOrNextCmd(&cmd); //product revision number + mc_respond(term); + debug_printf("received SD2PSXMAN_PING\n"); +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_get_status(void) +{ + uint8_t cmd; + //TODO + debug_printf("received SD2PSXMAN_GET_STATUS\n"); +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_get_card(void) +{ + uint8_t cmd; + int card = ps2_cardman_get_idx(); + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(card >> 8); receiveOrNextCmd(&cmd); //card upper 8 bits + mc_respond(card & 0xff); receiveOrNextCmd(&cmd); //card lower 8 bits + mc_respond(term); + debug_printf("received SD2PSXMAN_GET_CARD\n"); +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_set_card(void) +{ + uint8_t cmd; + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(0x0); receiveOrNextCmd(&cmd); //mode + sd2psxman_mode = cmd; + mc_respond(0x0); receiveOrNextCmd(&cmd); //card upper 8 bits + sd2psxman_cnum = cmd << 8; + mc_respond(0x0); receiveOrNextCmd(&cmd); //card lower 8 bits + sd2psxman_cnum |= cmd; + mc_respond(term); + + debug_printf("received SD2PSXMAN_SET_CARD mode: %i, num: %i\n", sd2psxman_mode, sd2psxman_cnum); + + sd2psxman_cmd = SD2PSXMAN_SET_CARD; //set after setting mode and cnum +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_get_channel(void) +{ + uint8_t cmd; + int chan = ps2_cardman_get_channel(); + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(chan >> 8); receiveOrNextCmd(&cmd); //channel upper 8 bits + mc_respond(chan & 0xff); receiveOrNextCmd(&cmd); //channel lower 8 bits + mc_respond(term); + debug_printf("received SD2PSXMAN_GET_CHANNEL\n"); +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_set_channel(void) +{ + uint8_t cmd; + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(0x0); receiveOrNextCmd(&cmd); //mode + sd2psxman_mode = cmd; + mc_respond(0x0); receiveOrNextCmd(&cmd); //channel upper 8 bits + sd2psxman_cnum = cmd << 8; + mc_respond(0x0); receiveOrNextCmd(&cmd); //channel lower 8 bits + sd2psxman_cnum |= cmd; + mc_respond(term); + + debug_printf("received SD2PSXMAN_SET_CHANNEL mode: %i, num: %i\n", sd2psxman_mode, sd2psxman_cnum); + + sd2psxman_cmd = SD2PSXMAN_SET_CHANNEL; //set after setting mode and cnum +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_get_gameid(void) +{ + uint8_t cmd; + uint8_t gameid_len = strlen(sd2psxman_gameid) + 1; //+1 null terminator + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(gameid_len); receiveOrNextCmd(&cmd); //gameid length + + for (int i = 0; i < gameid_len; i++) { + mc_respond(sd2psxman_gameid[i]); receiveOrNextCmd(&cmd); //gameid + } + + for (int i = 0; i < (250 - gameid_len); i++) { + mc_respond(0x0); receiveOrNextCmd(&cmd); //padding + } + + mc_respond(term); + + debug_printf("received SD2PSXMAN_GET_GAMEID\n"); +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_set_gameid(void) +{ + uint8_t cmd; + uint8_t gameid_len; + uint8_t received_id[252] = { 0 }; + char sanitized_game_id[11] = { 0 }; + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(0x0); receiveOrNextCmd(&cmd); //gameid length + gameid_len = cmd; + + for (int i = 0; i < gameid_len; i++) { + mc_respond(0x0); receiveOrNextCmd(&cmd); //gameid + received_id[i] = cmd; + } + + mc_respond(term); + + game_names_extract_title_id(received_id, sanitized_game_id, gameid_len, sizeof(sanitized_game_id)); + if (game_names_sanity_check_title_id(sanitized_game_id)) { + ps2_sd2psxman_set_gameid(sanitized_game_id); + sd2psxman_cmd = SD2PSXMAN_SET_GAMEID; + } + + debug_printf("received SD2PSXMAN_SET_GAMEID len %i, id: %s\n", gameid_len, sanitized_game_id); +} + +inline __attribute__((always_inline)) void ps2_sd2psxman_cmds_unmount_bootcard(void) +{ + uint8_t cmd; + mc_respond(0x0); receiveOrNextCmd(&cmd); //reserved byte + mc_respond(term); + + debug_printf("received SD2PSXMAN_UNMOUNT_BOOTCARD\n"); + + sd2psxman_cmd = SD2PSXMAN_UNMOUNT_BOOTCARD; +} \ No newline at end of file diff --git a/src/ps2/card_emu/ps2_sd2psxman_commands.h b/src/ps2/card_emu/ps2_sd2psxman_commands.h new file mode 100644 index 0000000..3c995d5 --- /dev/null +++ b/src/ps2/card_emu/ps2_sd2psxman_commands.h @@ -0,0 +1,28 @@ +#pragma once + +#define PS2_SD2PSXMAN_CMD_IDENTIFIER 0x8B + +#define SD2PSXMAN_PING 0x1 +#define SD2PSXMAN_GET_STATUS 0x2 +#define SD2PSXMAN_GET_CARD 0x3 +#define SD2PSXMAN_SET_CARD 0x4 +#define SD2PSXMAN_GET_CHANNEL 0x5 +#define SD2PSXMAN_SET_CHANNEL 0x6 +#define SD2PSXMAN_GET_GAMEID 0x7 +#define SD2PSXMAN_SET_GAMEID 0x8 + +#define SD2PSXMAN_UNMOUNT_BOOTCARD 0x30 + +#define SD2PSXMAN_MODE_NUM 0x0 +#define SD2PSXMAN_MODE_NEXT 0x1 +#define SD2PSXMAN_MODE_PREV 0x2 + +extern void ps2_sd2psxman_cmds_ping(void); +extern void ps2_sd2psxman_cmds_get_status(void); +extern void ps2_sd2psxman_cmds_get_card(void); +extern void ps2_sd2psxman_cmds_set_card(void); +extern void ps2_sd2psxman_cmds_get_channel(void); +extern void ps2_sd2psxman_cmds_set_channel(void); +extern void ps2_sd2psxman_cmds_get_gameid(void); +extern void ps2_sd2psxman_cmds_set_gameid(void); +extern void ps2_sd2psxman_cmds_unmount_bootcard(void); \ No newline at end of file diff --git a/src/ps2/ps2_cardman.c b/src/ps2/ps2_cardman.c index 07fe616..00bb85f 100644 --- a/src/ps2/ps2_cardman.c +++ b/src/ps2/ps2_cardman.c @@ -1,39 +1,48 @@ #include "ps2_cardman.h" -#include #include #include #include -#include "sd.h" +#include "card_emu/ps2_sd2psxman.h" #include "debug.h" +#include "game_names/game_names.h" +#include "hardware/timer.h" #include "ps2_psram.h" +#include "sd.h" #include "settings.h" -#include "hardware/timer.h" - - -#define PS2_DEFAULT_CARD_SIZE PS2_CARD_SIZE_8M -#define BLOCK_SIZE (512) +#define PS2_DEFAULT_CARD_SIZE PS2_CARD_SIZE_8M +#define BLOCK_SIZE (512) static uint8_t flushbuf[BLOCK_SIZE]; static int fd = -1; +#define MAX_GAME_NAME_LENGTH (127) +#define MAX_PREFIX_LENGTH (4) +#define MAX_GAME_ID_LENGTH (16) + static int card_idx; static int card_chan; static uint32_t card_size; static cardman_cb_t cardman_cb; +static char folder_name[MAX_GAME_ID_LENGTH]; static uint64_t cardprog_start; static size_t cardprog_pos; static int cardprog_wr; +static ps2_cardman_state_t cardman_state; + void ps2_cardman_init(void) { if (settings_get_ps2_autoboot()) { - card_idx = IDX_BOOT; + card_idx = PS2_CARD_IDX_SPECIAL; + cardman_state = PS2_CM_STATE_BOOT; card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "BOOT"); } else { card_idx = settings_get_ps2_card(); card_chan = settings_get_ps2_channel(); + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); } } @@ -57,10 +66,8 @@ void ps2_cardman_flush(void) { static void ensuredirs(void) { char cardpath[32]; - if (IDX_BOOT == card_idx) - snprintf(cardpath, sizeof(cardpath), "MemoryCards/PS2/BOOT"); - else - snprintf(cardpath, sizeof(cardpath), "MemoryCards/PS2/Card%d", card_idx); + + snprintf(cardpath, sizeof(cardpath), "MemoryCards/PS2/%s", folder_name); sd_mkdir("MemoryCards"); sd_mkdir("MemoryCards/PS2"); @@ -71,113 +78,72 @@ static void ensuredirs(void) { } static const uint8_t block0[384] = { - 0x53, 0x6F, 0x6E, 0x79, 0x20, 0x50, 0x53, 0x32, 0x20, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x20, - 0x43, 0x61, 0x72, 0x64, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x61, 0x74, 0x20, 0x31, 0x2E, 0x32, 0x2E, - 0x30, 0x2E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x10, 0x00, 0x00, 0xFF, - 0x00, 0x20, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0xC7, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0x03, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x02, 0x2B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF -}; + 0x53, 0x6F, 0x6E, 0x79, 0x20, 0x50, 0x53, 0x32, 0x20, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x20, 0x43, 0x61, 0x72, 0x64, 0x20, 0x46, 0x6F, 0x72, 0x6D, 0x61, + 0x74, 0x20, 0x31, 0x2E, 0x32, 0x2E, 0x30, 0x2E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x20, 0x00, 0x00, + 0x29, 0x00, 0x00, 0x00, 0xC7, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0x00, 0x00, 0xFE, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x2B, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; static const uint8_t block2000[128] = { - 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00 -}; + 0x09, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00}; static const uint8_t blockA400[512] = { - 0x27, 0x84, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; + 0x27, 0x84, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static const uint8_t blockA600[512] = { - 0x26, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - + 0x26, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0x31, 0x0C, 0x18, 0x0A, 0xE6, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static void genblock(size_t pos, void *vbuf) { uint8_t *buf = vbuf; @@ -189,7 +155,7 @@ static void genblock(size_t pos, void *vbuf) { } else if (pos == 0x2000) { memcpy(buf, block2000, sizeof(block2000)); } else if (pos >= 0x2400 && pos < 0xA400) { - for (size_t i = 0; i < BLOCK_SIZE/4; ++i) { + for (size_t i = 0; i < BLOCK_SIZE / 4; ++i) { uint32_t val = 0x7FFFFFFF; memcpy(&buf[i * 4], &val, sizeof(val)); } @@ -210,10 +176,11 @@ void ps2_cardman_open(void) { char path[64]; ensuredirs(); - if (IDX_BOOT == card_idx) { - snprintf(path, sizeof(path), "MemoryCards/PS2/BOOT/BootCard.mcd"); - } else { - snprintf(path, sizeof(path), "MemoryCards/PS2/Card%d/Card%d-%d.mcd", card_idx, card_idx, card_chan); + + if (PS2_CM_STATE_BOOT == cardman_state) + snprintf(path, sizeof(path), "MemoryCards/PS2/%s/BootCard.mcd", folder_name); + else { + snprintf(path, sizeof(path), "MemoryCards/PS2/%s/%s-%d.mcd", folder_name, folder_name, card_chan); /* this is ok to do on every boot because it wouldn't update if the value is the same as currently stored */ settings_set_ps2_card(card_idx); settings_set_ps2_channel(card_chan); @@ -221,7 +188,6 @@ void ps2_cardman_open(void) { printf("Switching to card path = %s\n", path); - if (!sd_exists(path)) { cardprog_wr = 1; fd = sd_open(path, O_RDWR | O_CREAT | O_TRUNC); @@ -236,7 +202,7 @@ void ps2_cardman_open(void) { if (PS2_DEFAULT_CARD_SIZE == PS2_CARD_SIZE_8M) genblock(pos, flushbuf); else - memset(flushbuf, 0xFF, sizeof(flushbuf)/sizeof(flushbuf[0])); + memset(flushbuf, 0xFF, sizeof(flushbuf) / sizeof(flushbuf[0])); if (sd_write(fd, flushbuf, BLOCK_SIZE) != BLOCK_SIZE) fatal("cannot init memcard"); psram_write(pos, flushbuf, BLOCK_SIZE); @@ -250,8 +216,7 @@ void ps2_cardman_open(void) { uint64_t end = time_us_64(); printf("OK!\n"); - printf("took = %.2f s; SD write speed = %.2f kB/s\n", (end - cardprog_start) / 1e6, - 1000000.0 * PS2_DEFAULT_CARD_SIZE / (end - cardprog_start) / 1024); + printf("took = %.2f s; SD write speed = %.2f kB/s\n", (end - cardprog_start) / 1e6, 1000000.0 * PS2_DEFAULT_CARD_SIZE / (end - cardprog_start) / 1024); } else { cardprog_wr = 0; fd = sd_open(path, O_RDWR); @@ -260,11 +225,8 @@ void ps2_cardman_open(void) { fatal("cannot open card"); card_size = sd_filesize(fd); - if ((card_size != PS2_CARD_SIZE_512K) - && (card_size != PS2_CARD_SIZE_1M) - && (card_size != PS2_CARD_SIZE_2M) - && (card_size != PS2_CARD_SIZE_4M) - && (card_size != PS2_CARD_SIZE_8M)) + if ((card_size != PS2_CARD_SIZE_512K) && (card_size != PS2_CARD_SIZE_1M) && (card_size != PS2_CARD_SIZE_2M) && (card_size != PS2_CARD_SIZE_4M) && + (card_size != PS2_CARD_SIZE_8M)) fatal("Card %d Channel %d is corrupted", card_idx, card_chan); /* read 8 megs of card image */ @@ -282,8 +244,7 @@ void ps2_cardman_open(void) { uint64_t end = time_us_64(); printf("OK!\n"); - printf("took = %.2f s; SD read speed = %.2f kB/s\n", (end - cardprog_start) / 1e6, - 1000000.0 * card_size / (end - cardprog_start) / 1024); + printf("took = %.2f s; SD read speed = %.2f kB/s\n", (end - cardprog_start) / 1e6, 1000000.0 * card_size / (end - cardprog_start) / 1024); } } @@ -295,59 +256,145 @@ void ps2_cardman_close(void) { fd = -1; } +void ps2_cardman_set_channel(uint16_t chan_num) { + if ((PS2_CM_STATE_NORMAL == cardman_state) || (PS2_CM_STATE_GAMEID == cardman_state)) { + if (chan_num <= CHAN_MAX && chan_num >= CHAN_MIN) { + card_chan = chan_num; + } + } else { + card_idx = settings_get_ps2_card(); + card_chan = settings_get_ps2_channel(); + } + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); +} + void ps2_cardman_next_channel(void) { - if (card_idx != IDX_BOOT) { + if ((PS2_CM_STATE_NORMAL == cardman_state) || (PS2_CM_STATE_GAMEID == cardman_state)) { card_chan += 1; if (card_chan > CHAN_MAX) card_chan = CHAN_MIN; } else { card_idx = settings_get_ps2_card(); card_chan = settings_get_ps2_channel(); + cardman_state = PS2_CM_STATE_NORMAL; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); } } void ps2_cardman_prev_channel(void) { - if (card_idx != IDX_BOOT) { + if ((PS2_CM_STATE_NORMAL == cardman_state) || (PS2_CM_STATE_GAMEID == cardman_state)) { card_chan -= 1; if (card_chan < CHAN_MIN) card_chan = CHAN_MAX; } else { card_idx = settings_get_ps2_card(); card_chan = settings_get_ps2_channel(); + cardman_state = PS2_CM_STATE_NORMAL; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); } } -void ps2_cardman_next_idx(void) { - if (card_idx != IDX_BOOT) { - card_idx += 1; +void ps2_cardman_set_idx(uint16_t idx_num) { + if ((idx_num >= IDX_MIN) && (idx_num <= UINT16_MAX)) { + card_idx = idx_num; card_chan = CHAN_MIN; + } + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); +} + +static void ps2_cardman_special_idx(int newIndx) { + char parent_id[MAX_GAME_ID_LENGTH] = { 0x00 }; + game_names_get_parent(sd2psxman_gameid, parent_id); + debug_printf("Parent ID is %s, State is %i, new Index: %i\n", parent_id, cardman_state, newIndx); + if (PS2_CM_STATE_NORMAL == cardman_state) { + if (parent_id[0]) { + card_idx = PS2_CARD_IDX_SPECIAL; + cardman_state = PS2_CM_STATE_GAMEID; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "%s", parent_id); + } else if (settings_get_ps2_autoboot()) { + card_idx = PS2_CARD_IDX_SPECIAL; + cardman_state = PS2_CM_STATE_BOOT; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "BOOT"); + } else { + cardman_state = PS2_CM_STATE_NORMAL; + card_idx = IDX_MIN; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); + } + } else if (PS2_CM_STATE_BOOT == cardman_state) { + if ((newIndx > PS2_CARD_IDX_SPECIAL) && (parent_id[0])) { + card_idx = PS2_CARD_IDX_SPECIAL; + cardman_state = PS2_CM_STATE_GAMEID; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "%s", parent_id); + } else { + card_idx = settings_get_ps2_card(); + card_chan = settings_get_ps2_channel(); + cardman_state = PS2_CM_STATE_NORMAL; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); + } + } else if (PS2_CM_STATE_GAMEID == cardman_state) { + if ((newIndx < PS2_CARD_IDX_SPECIAL) && (settings_get_ps2_autoboot())) { + // Prev Pressed and Boot available + card_idx = PS2_CARD_IDX_SPECIAL; + cardman_state = PS2_CM_STATE_BOOT; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "BOOT"); + } else { + card_idx = settings_get_ps2_card(); + card_chan = settings_get_ps2_channel(); + cardman_state = PS2_CM_STATE_NORMAL; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); + } + } +} + +void ps2_cardman_next_idx(void) { + int newIdx = card_idx + 1; + if (PS2_CM_STATE_NORMAL != cardman_state) { + ps2_cardman_special_idx(newIdx); } else { - card_idx = settings_get_ps2_card(); - card_chan = settings_get_ps2_channel(); + card_idx = (newIdx > (int)UINT16_MAX) ? UINT16_MAX : newIdx; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); } } void ps2_cardman_prev_idx(void) { - if (card_idx != IDX_BOOT) { - int minIndex = (settings_get_ps2_autoboot() ? IDX_BOOT : IDX_MIN); - card_idx -= 1; - card_chan = CHAN_MIN; - if (card_idx < minIndex) - card_idx = minIndex; + int newIdx = card_idx - 1; + if ((PS2_CM_STATE_NORMAL != cardman_state) || (PS2_CARD_IDX_SPECIAL == newIdx)) { + ps2_cardman_special_idx(newIdx); } else { - card_idx = settings_get_ps2_card(); - card_chan = settings_get_ps2_channel(); + card_idx = newIdx; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "Card%d", card_idx); } } -int ps2_cardman_get_idx(void) { - return card_idx; +int ps2_cardman_get_idx(void) { + return (cardman_state == PS2_CM_STATE_NORMAL) ? card_idx : PS2_CARD_IDX_SPECIAL; } int ps2_cardman_get_channel(void) { return card_chan; } +void ps2_cardman_set_gameid(const char *const card_game_id) { + char new_folder_name[MAX_GAME_ID_LENGTH]; + if (card_game_id[0]) { + char parent_id[MAX_GAME_ID_LENGTH]; + game_names_get_parent(card_game_id, parent_id); + snprintf(new_folder_name, sizeof(new_folder_name), "%s", parent_id); + if (strcmp(new_folder_name, folder_name) != 0) { + card_idx = PS2_CARD_IDX_SPECIAL; + cardman_state = PS2_CM_STATE_GAMEID; + card_chan = CHAN_MIN; + snprintf(folder_name, sizeof(folder_name), "%s", parent_id); + } + } +} + void ps2_cardman_set_progress_cb(cardman_cb_t func) { cardman_cb = func; } @@ -355,12 +402,19 @@ void ps2_cardman_set_progress_cb(cardman_cb_t func) { char *ps2_cardman_get_progress_text(void) { static char progress[32]; - snprintf(progress, sizeof(progress), "%s %.2f kB/s", cardprog_wr ? "Wr" : "Rd", - 1000000.0 * cardprog_pos / (time_us_64() - cardprog_start) / 1024); + snprintf(progress, sizeof(progress), "%s %.2f kB/s", cardprog_wr ? "Wr" : "Rd", 1000000.0 * cardprog_pos / (time_us_64() - cardprog_start) / 1024); return progress; } uint32_t ps2_cardman_get_card_size(void) { return card_size; +} + +const char *ps2_cardman_get_folder_name(void) { + return folder_name; +} + +ps2_cardman_state_t ps2_cardman_get_state(void) { + return cardman_state; } \ No newline at end of file diff --git a/src/ps2/ps2_cardman.h b/src/ps2/ps2_cardman.h index d6fd745..705cc8b 100644 --- a/src/ps2/ps2_cardman.h +++ b/src/ps2/ps2_cardman.h @@ -8,6 +8,14 @@ #define PS2_CARD_SIZE_1M (1024 * 1024) #define PS2_CARD_SIZE_512K (512 * 1024) +#define PS2_CARD_IDX_SPECIAL 0 + +typedef enum { + PS2_CM_STATE_BOOT, + PS2_CM_STATE_GAMEID, + PS2_CM_STATE_NORMAL +} ps2_cardman_state_t; + void ps2_cardman_init(void); int ps2_cardman_write_sector(int sector, void *buf512); void ps2_cardman_flush(void); @@ -17,12 +25,21 @@ int ps2_cardman_get_idx(void); int ps2_cardman_get_channel(void); uint32_t ps2_cardman_get_card_size(void); +void ps2_cardman_set_channel(uint16_t num); void ps2_cardman_next_channel(void); void ps2_cardman_prev_channel(void); + +void ps2_cardman_set_idx(uint16_t num); void ps2_cardman_next_idx(void); void ps2_cardman_prev_idx(void); typedef void (*cardman_cb_t)(int); void ps2_cardman_set_progress_cb(cardman_cb_t func); -char *ps2_cardman_get_progress_text(void); \ No newline at end of file +char *ps2_cardman_get_progress_text(void); + +void ps2_cardman_set_gameid(const char* game_id); +const char* ps2_cardman_get_gameid(void); +const char* ps2_cardman_get_gamename(void); +const char* ps2_cardman_get_folder_name(void); +ps2_cardman_state_t ps2_cardman_get_state(void); diff --git a/src/ps2/ps2_dirty.c b/src/ps2/ps2_dirty.c index ee68330..94e9261 100644 --- a/src/ps2/ps2_dirty.c +++ b/src/ps2/ps2_dirty.c @@ -6,6 +6,8 @@ #define dirty_heap bigmem.ps2.dirty_heap #define dirty_map bigmem.ps2.dirty_map +#include +#include #include spin_lock_t *ps2_dirty_spin_lock; diff --git a/src/ps2/ps2_exploit.h b/src/ps2/ps2_exploit.h index 50f8310..26d9337 100644 --- a/src/ps2/ps2_exploit.h +++ b/src/ps2/ps2_exploit.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/src/ps2/ps2_memory_card.c b/src/ps2/ps2_memory_card.c deleted file mode 100644 index e075362..0000000 --- a/src/ps2/ps2_memory_card.c +++ /dev/null @@ -1,442 +0,0 @@ -#include "hardware/gpio.h" -#include "hardware/regs/addressmap.h" -#include "hardware/timer.h" -#include "hardware/flash.h" -#include "hardware/dma.h" -#include "pico/platform.h" - -#include "config.h" -#include "ps2_mc_spi.pio.h" -#include "flashmap.h" -#include "debug.h" -#include "keystore.h" -#include "des.h" - -#include "ps2_dirty.h" -#include "ps2_psram.h" -#include "ps2_pio_qspi.h" -#include "ps2_cardman.h" -#include "ps2_exploit.h" - -#include -#include - - -// #define DEBUG_MC_PROTOCOL - -uint64_t us_startup; - -int byte_count; -volatile int reset; -int ignore; -uint8_t flag; -bool flash_mode = false; - -typedef struct { - uint32_t offset; - uint32_t sm; -} pio_t; - -pio_t cmd_reader, dat_writer, dat_writer_slow, clock_probe; - -#define ERASE_SECTORS 16 -#define CARD_SIZE (8 * 1024 * 1024) - -uint8_t term = 0xFF; -uint32_t read_sector, write_sector, erase_sector; -struct { - uint32_t prefix; - uint8_t buf[528]; -} readtmp; -uint8_t *eccptr; -uint8_t writetmp[528]; -int is_write, is_dma_read; -uint32_t readptr, writeptr; -static volatile int mc_exit_request, mc_exit_response, mc_enter_request, mc_enter_response; -static uint8_t hostkey[9]; - -static inline void __time_critical_func(read_mc)(uint32_t addr, void *buf, size_t sz) { - if (flash_mode) { - ps2_exploit_read(addr, buf, sz); - ps2_dirty_unlock(); - } else { - psram_read_dma(addr, buf, sz); - } -} - -static inline void __time_critical_func(write_mc)(uint32_t addr, void *buf, size_t sz) { - if (!flash_mode) { - psram_write(addr, buf, sz); - } else { - ps2_dirty_unlock(); - } -} - -static inline void __time_critical_func(RAM_pio_sm_drain_tx_fifo)(PIO pio, uint sm) { - uint instr = (pio->sm[sm].shiftctrl & PIO_SM0_SHIFTCTRL_AUTOPULL_BITS) ? pio_encode_out(pio_null, 32) : - pio_encode_pull(false, false); - while (!pio_sm_is_tx_fifo_empty(pio, sm)) { - pio_sm_exec(pio, sm, instr); - } -} - -static void __time_critical_func(reset_pio)(void) { - pio_set_sm_mask_enabled(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm) | (1 << dat_writer_slow.sm) | (1 << clock_probe.sm), false); - pio_restart_sm_mask(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm) | (1 << dat_writer_slow.sm) | (1 << clock_probe.sm)); - - pio_sm_exec(pio0, cmd_reader.sm, pio_encode_jmp(cmd_reader.offset)); - pio_sm_exec(pio0, dat_writer.sm, pio_encode_jmp(dat_writer.offset)); - pio_sm_exec(pio0, dat_writer_slow.sm, pio_encode_jmp(dat_writer_slow.offset)); - pio_sm_exec(pio0, clock_probe.sm, pio_encode_jmp(clock_probe.offset)); - - pio_sm_clear_fifos(pio0, cmd_reader.sm); - RAM_pio_sm_drain_tx_fifo(pio0, dat_writer.sm); - RAM_pio_sm_drain_tx_fifo(pio0, dat_writer_slow.sm); - pio_sm_clear_fifos(pio0, clock_probe.sm); - - pio_enable_sm_mask_in_sync(pio0, (1 << cmd_reader.sm) | (1 << dat_writer.sm) | (1 << dat_writer_slow.sm) | (1 << clock_probe.sm)); - - reset = 1; -} - -static void __time_critical_func(init_pio)(void) { - /* Set all pins as floating inputs */ - gpio_set_dir(PIN_PSX_ACK, 0); - gpio_set_dir(PIN_PSX_SEL, 0); - gpio_set_dir(PIN_PSX_CLK, 0); - gpio_set_dir(PIN_PSX_CMD, 0); - gpio_set_dir(PIN_PSX_DAT, 0); - gpio_disable_pulls(PIN_PSX_ACK); - gpio_disable_pulls(PIN_PSX_SEL); - gpio_disable_pulls(PIN_PSX_CLK); - gpio_disable_pulls(PIN_PSX_CMD); - gpio_disable_pulls(PIN_PSX_DAT); - - cmd_reader.offset = pio_add_program(pio0, &cmd_reader_program); - cmd_reader.sm = pio_claim_unused_sm(pio0, true); - - dat_writer.offset = pio_add_program(pio0, &dat_writer_program); - dat_writer.sm = pio_claim_unused_sm(pio0, true); - - dat_writer_slow.offset = pio_add_program(pio0, &dat_writer_slow_program); - dat_writer_slow.sm = pio_claim_unused_sm(pio0, true); - - clock_probe.offset = pio_add_program(pio0, &clock_probe_program); - clock_probe.sm = pio_claim_unused_sm(pio0, true); - - cmd_reader_program_init(pio0, cmd_reader.sm, cmd_reader.offset); - dat_writer_program_init(pio0, dat_writer.sm, dat_writer.offset); - dat_writer_slow_program_init(pio0, dat_writer_slow.sm, dat_writer_slow.offset); - clock_probe_program_init(pio0, clock_probe.sm, clock_probe.offset); -} - -static void __time_critical_func(card_deselected)(uint gpio, uint32_t event_mask) { - if (gpio == PIN_PSX_SEL && (event_mask & GPIO_IRQ_EDGE_RISE)) { - reset_pio(); - } -} - -#define recv() do { \ - while ( \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - 1) { \ - if (reset) \ - goto NEXTCMD; \ - } \ - cmd = (uint8_t) (pio_sm_get(pio0, cmd_reader.sm) >> 24); \ -} while (0); - -#define recvfirst() do { \ - while ( \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - pio_sm_is_rx_fifo_empty(pio0, cmd_reader.sm) && \ - 1) { \ - if (reset) \ - goto NEXTCMD; \ - if (mc_exit_request) \ - goto EXIT_REQUEST; \ - } \ - cmd = (uint8_t) (pio_sm_get(pio0, cmd_reader.sm) >> 24); \ -} while (0); - -static inline uint32_t __time_critical_func(probe_clock)(void) { - return pio_sm_get_blocking(pio0, clock_probe.sm); -} - -static inline void __time_critical_func(mc_respond_fast)(uint8_t ch) { - pio_sm_put_blocking(pio0, dat_writer.sm, ch); -} - -static inline void __time_critical_func(mc_respond_slow)(uint8_t ch) { - pio_sm_put_blocking(pio0, dat_writer_slow.sm, ch); -} - -static uint8_t Table[] = { - 0x00, 0x87, 0x96, 0x11, 0xa5, 0x22, 0x33, 0xb4,0xb4, 0x33, 0x22, 0xa5, 0x11, 0x96, 0x87, 0x00, - 0xc3, 0x44, 0x55, 0xd2, 0x66, 0xe1, 0xf0, 0x77,0x77, 0xf0, 0xe1, 0x66, 0xd2, 0x55, 0x44, 0xc3, - 0xd2, 0x55, 0x44, 0xc3, 0x77, 0xf0, 0xe1, 0x66,0x66, 0xe1, 0xf0, 0x77, 0xc3, 0x44, 0x55, 0xd2, - 0x11, 0x96, 0x87, 0x00, 0xb4, 0x33, 0x22, 0xa5,0xa5, 0x22, 0x33, 0xb4, 0x00, 0x87, 0x96, 0x11, - 0xe1, 0x66, 0x77, 0xf0, 0x44, 0xc3, 0xd2, 0x55,0x55, 0xd2, 0xc3, 0x44, 0xf0, 0x77, 0x66, 0xe1, - 0x22, 0xa5, 0xb4, 0x33, 0x87, 0x00, 0x11, 0x96,0x96, 0x11, 0x00, 0x87, 0x33, 0xb4, 0xa5, 0x22, - 0x33, 0xb4, 0xa5, 0x22, 0x96, 0x11, 0x00, 0x87,0x87, 0x00, 0x11, 0x96, 0x22, 0xa5, 0xb4, 0x33, - 0xf0, 0x77, 0x66, 0xe1, 0x55, 0xd2, 0xc3, 0x44,0x44, 0xc3, 0xd2, 0x55, 0xe1, 0x66, 0x77, 0xf0, - 0xf0, 0x77, 0x66, 0xe1, 0x55, 0xd2, 0xc3, 0x44,0x44, 0xc3, 0xd2, 0x55, 0xe1, 0x66, 0x77, 0xf0, - 0x33, 0xb4, 0xa5, 0x22, 0x96, 0x11, 0x00, 0x87,0x87, 0x00, 0x11, 0x96, 0x22, 0xa5, 0xb4, 0x33, - 0x22, 0xa5, 0xb4, 0x33, 0x87, 0x00, 0x11, 0x96,0x96, 0x11, 0x00, 0x87, 0x33, 0xb4, 0xa5, 0x22, - 0xe1, 0x66, 0x77, 0xf0, 0x44, 0xc3, 0xd2, 0x55,0x55, 0xd2, 0xc3, 0x44, 0xf0, 0x77, 0x66, 0xe1, - 0x11, 0x96, 0x87, 0x00, 0xb4, 0x33, 0x22, 0xa5,0xa5, 0x22, 0x33, 0xb4, 0x00, 0x87, 0x96, 0x11, - 0xd2, 0x55, 0x44, 0xc3, 0x77, 0xf0, 0xe1, 0x66,0x66, 0xe1, 0xf0, 0x77, 0xc3, 0x44, 0x55, 0xd2, - 0xc3, 0x44, 0x55, 0xd2, 0x66, 0xe1, 0xf0, 0x77,0x77, 0xf0, 0xe1, 0x66, 0xd2, 0x55, 0x44, 0xc3, - 0x00, 0x87, 0x96, 0x11, 0xa5, 0x22, 0x33, 0xb4,0xb4, 0x33, 0x22, 0xa5, 0x11, 0x96, 0x87, 0x00 -}; - -void calcECC(uint8_t *ecc, const uint8_t *data) -{ - int i, c; - - ecc[0] = ecc[1] = ecc[2] = 0; - - for (i = 0 ; i < 0x80 ; i ++) { - c = Table[data[i]]; - - ecc[0] ^= c; - if (c & 0x80) { - ecc[1] ^= ~i; - ecc[2] ^= i; - } - } - ecc[0] = ~ecc[0]; - ecc[0] &= 0x77; - - ecc[1] = ~ecc[1]; - ecc[1] &= 0x7f; - - ecc[2] = ~ecc[2]; - ecc[2] &= 0x7f; - - return; -} - -// keysource and key are self generated values -uint8_t keysource[] = { 0xf5, 0x80, 0x95, 0x3c, 0x4c, 0x84, 0xa9, 0xc0 }; -uint8_t dex_key[16] = { 0x17, 0x39, 0xd3, 0xbc, 0xd0, 0x2c, 0x18, 0x07, 0x4b, 0x17, 0xf0, 0xea, 0xc4, 0x66, 0x30, 0xf9 }; -uint8_t cex_key[16] = { 0x06, 0x46, 0x7a, 0x6c, 0x5b, 0x9b, 0x82, 0x77, 0x0d, 0xdf, 0xe9, 0x7e, 0x24, 0x5b, 0x9f, 0xca }; -uint8_t *key = cex_key; - -static uint8_t iv[8]; -static uint8_t seed[8]; -static uint8_t nonce[8]; -static uint8_t MechaChallenge3[8]; -static uint8_t MechaChallenge2[8]; -static uint8_t MechaChallenge1[8]; -static uint8_t CardResponse1[8]; -static uint8_t CardResponse2[8]; -static uint8_t CardResponse3[8]; - -static void __time_critical_func(desEncrypt)(void *key, void *data) -{ - DesContext dc; - desInit(&dc, (uint8_t *) key, 8); - desEncryptBlock(&dc, (uint8_t *) data, (uint8_t *) data); -} - -static void __time_critical_func(desDecrypt)(void *key, void *data) -{ - DesContext dc; - desInit(&dc, (uint8_t *) key, 8); - desDecryptBlock(&dc, (uint8_t *) data, (uint8_t *) data); -} - -static void __time_critical_func(doubleDesEncrypt)(void *key, void *data) -{ - desEncrypt(key, data); - desDecrypt(&((uint8_t *) key)[8], data); - desEncrypt(key, data); -} - -static void __time_critical_func(doubleDesDecrypt)(void *key, void *data) -{ - desDecrypt(key, data); - desEncrypt(&((uint8_t *) key)[8], data); - desDecrypt(key, data); -} - -static void __time_critical_func(xor_bit)(const void* a, const void* b, void* Result, size_t Length) { - size_t i; - for (i = 0; i < Length; i++) { - ((uint8_t*)Result)[i] = ((uint8_t*)a)[i] ^ ((uint8_t*)b)[i]; - } -} - -static void __time_critical_func(generateIvSeedNonce)() { - for (int i = 0; i < 8; i++) { - iv[i] = 0x42; - seed[i] = keysource[i] ^ iv[i]; - nonce[i] = 0x42; - } -} - -static void __time_critical_func(generateResponse)() { - doubleDesDecrypt(key, MechaChallenge1); - uint8_t random[8] = { 0 }; - xor_bit(MechaChallenge1, ps2_civ, random, 8); - - // MechaChallenge2 and MechaChallenge3 let's the card verify the console - - xor_bit(nonce, ps2_civ, CardResponse1, 8); - - doubleDesEncrypt(key, CardResponse1); - - xor_bit(random, CardResponse1, CardResponse2, 8); - doubleDesEncrypt(key, CardResponse2); - - uint8_t CardKey[] = { 'M', 'e', 'c', 'h', 'a', 'P', 'w', 'n' }; - xor_bit(CardKey, CardResponse2, CardResponse3, 8); - doubleDesEncrypt(key, CardResponse3); -} - -static void __time_critical_func(mc_main_loop)(void) { - while (1) { - uint8_t cmd, ch; - -NEXTCMD: - while (!reset && !reset && !reset && !reset && !reset) { - if (mc_exit_request) - goto EXIT_REQUEST; - } - reset = 0; - - recvfirst(); - - if (cmd == 0x81) { - if (probe_clock()) { -#define send mc_respond_fast -#include "ps2_memory_card.in.c" -#undef send - } else { -#define send mc_respond_slow -#include "ps2_memory_card.in.c" -#undef send - } - } else { - // not for us - continue; - } - } - -EXIT_REQUEST: - mc_exit_response = 1; -} - -static void __no_inline_not_in_flash_func(mc_main)(void) { - while (1) { - while (!mc_enter_request) - {} - mc_enter_response = 1; - - mc_main_loop(); - } -} - -static gpio_irq_callback_t callbacks[NUM_CORES]; - -static void __time_critical_func(RAM_gpio_acknowledge_irq)(uint gpio, uint32_t events) { - check_gpio_param(gpio); - iobank0_hw->intr[gpio / 8] = events << (4 * (gpio % 8)); -} - -static void __time_critical_func(RAM_gpio_default_irq_handler)(void) { - uint core = get_core_num(); - gpio_irq_callback_t callback = callbacks[core]; - io_irq_ctrl_hw_t *irq_ctrl_base = core ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl; - for (uint gpio = 0; gpio < NUM_BANK0_GPIOS; gpio+=8) { - uint32_t events8 = irq_ctrl_base->ints[gpio >> 3u]; - // note we assume events8 is 0 for non-existent GPIO - for(uint i=gpio;events8 && i>= 4; - } - } -} - -static void my_gpio_set_irq_callback(gpio_irq_callback_t callback) { - uint core = get_core_num(); - if (callbacks[core]) { - if (!callback) { - irq_remove_handler(IO_IRQ_BANK0, RAM_gpio_default_irq_handler); - } - callbacks[core] = callback; - } else if (callback) { - callbacks[core] = callback; - irq_add_shared_handler(IO_IRQ_BANK0, RAM_gpio_default_irq_handler, GPIO_IRQ_CALLBACK_ORDER_PRIORITY); - } -} - -static void my_gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback) { - gpio_set_irq_enabled(gpio, events, enabled); - my_gpio_set_irq_callback(callback); - if (enabled) irq_set_enabled(IO_IRQ_BANK0, true); -} - -void ps2_memory_card_main(void) { - init_pio(); - generateIvSeedNonce(); - - us_startup = time_us_64(); - debug_printf("Secondary core!\n"); - - my_gpio_set_irq_enabled_with_callback(PIN_PSX_SEL, GPIO_IRQ_EDGE_RISE, 1, card_deselected); - - gpio_set_slew_rate(PIN_PSX_DAT, GPIO_SLEW_RATE_FAST); - gpio_set_drive_strength(PIN_PSX_DAT, GPIO_DRIVE_STRENGTH_12MA); - mc_main(); -} - -static int memcard_running; - -void ps2_memory_card_exit(void) { - if (!memcard_running) - return; - - mc_exit_request = 1; - while (!mc_exit_response) - {} - mc_exit_request = mc_exit_response = 0; - memcard_running = 0; -} - -void ps2_memory_card_enter(void) { - if (flash_mode) { - ps2_memory_card_exit(); - } else if (memcard_running) - return; - - mc_enter_request = 1; - while (!mc_enter_response) - {} - mc_enter_request = mc_enter_response = 0; - memcard_running = 1; - flash_mode = false; -} - -void ps2_memory_card_enter_flash(void) { - mc_enter_request = 1; - while (!mc_enter_response) - {} - mc_enter_request = mc_enter_response = 0; - memcard_running = 1; - flash_mode = true; -} diff --git a/src/ps2/ps2_memory_card.in.c b/src/ps2/ps2_memory_card.in.c deleted file mode 100644 index f29391d..0000000 --- a/src/ps2/ps2_memory_card.in.c +++ /dev/null @@ -1,524 +0,0 @@ -#include -#include -#define XOR8(a) (a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5] ^ a[6] ^ a[7]) -#define ARG8(a) a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7] - -/* resp to 0x81 */ -send(0xFF); - -/* sub cmd */ -recv(); -ch = cmd; -#ifdef DEBUG_MC_PROTOCOL -if (ch != 0x42 && ch != 0x43) - debug_printf("> %02X\n", ch); -#endif - -if (ch == 0x11) { - send(0x2B); recv(); - send(term); -} else if (ch == 0x12) { - send(0x2B); recv(); - send(term); -} else if (ch == 0x21) { - /* set address for erase */ - union { - uint8_t a[4]; - uint32_t addr; - } raw; - uint8_t ck; - send(0xFF); recv(); raw.a[0] = cmd; - send(0xFF); recv(); raw.a[1] = cmd; - send(0xFF); recv(); raw.a[2] = cmd; - send(0xFF); recv(); raw.a[3] = cmd; - send(0xFF); recv(); ck = cmd; - send(0x2B); recv(); - (void)ck; // TODO: validate checksum - erase_sector = raw.addr; - send(term); -} else if (cmd == 0x22) { - /* set address for write */ - union { - uint8_t a[4]; - uint32_t addr; - } raw; - uint8_t ck; - send(0xFF); recv(); raw.a[0] = cmd; - send(0xFF); recv(); raw.a[1] = cmd; - send(0xFF); recv(); raw.a[2] = cmd; - send(0xFF); recv(); raw.a[3] = cmd; - send(0xFF); recv(); ck = cmd; - send(0x2B); recv(); - (void)ck; // TODO: validate checksum - write_sector = raw.addr; - is_write = 1; - writeptr = 0; - send(term); -} else if (ch == 0x23) { - /* set address for read */ - union { - uint8_t a[4]; - uint32_t addr; - } raw; - uint8_t ck; - send(0xFF); recv(); raw.a[0] = cmd; - send(0xFF); recv(); raw.a[1] = cmd; - send(0xFF); recv(); raw.a[2] = cmd; - send(0xFF); recv(); raw.a[3] = cmd; - send(0xFF); recv(); ck = cmd; - send(0x2B); recv(); - (void)ck; // TODO: validate checksum - read_sector = raw.addr; - if (read_sector * 512 + 512 <= ps2_cardman_get_card_size()) { - ps2_dirty_lockout_renew(); - /* the spinlock will be unlocked by the DMA irq once all data is tx'd */ - ps2_dirty_lock(); - read_mc(read_sector * 512, &readtmp, 512+4); - // dma_channel_wait_for_finish_blocking(0); - // dma_channel_wait_for_finish_blocking(1); - } - readptr = 0; - - eccptr = &readtmp.buf[512]; - memset(eccptr, 0, 16); - - send(term); -} else if (ch == 0x26) { - /* GET_SPECS ? */ - send(0x2B); recv(); - uint32_t sector_count = (flash_mode) ? PS2_CARD_SIZE_1M / 512 : (uint32_t)(ps2_cardman_get_card_size() / 512); - - uint8_t specs[] = { 0x00, 0x02, ERASE_SECTORS, 0x00, 0x00, 0x40, 0x00, 0x00 }; - specs[4] = (uint8_t)(sector_count & 0xFF); - specs[5] = (uint8_t)((sector_count >> 8) & 0xFF); - specs[6] = (uint8_t)((sector_count >> 16) & 0xFF); - specs[7] = (uint8_t)((sector_count >> 24) & 0xFF); - - send(specs[0]); recv(); - send(specs[1]); recv(); - send(specs[2]); recv(); - send(specs[3]); recv(); - send(specs[4]); recv(); - send(specs[5]); recv(); - send(specs[6]); recv(); - send(specs[7]); recv(); - send(XOR8(specs)); recv(); - send(term); -} else if (ch == 0x27) { - /* SET_TERMINATOR */ - send(0xFF); - recv(); term = cmd; - send(0x2B); recv(); - send(term); -} else if (ch == 0x28) { - /* GET_TERMINATOR */ - send(0x2B); recv(); - send(term); recv(); - send(term); -} else if (ch == 0x42) { - /* write data */ - uint8_t sz; - send(0xFF); recv(); sz = cmd; - send(0xFF); - -#ifdef DEBUG_MC_PROTOCOL - debug_printf("> %02X %02X\n", ch, sz); -#endif - - uint8_t ck = 0; - uint8_t b; - - for (int i = 0; i < sz; ++i) { - recv(); b = cmd; - if (writeptr < sizeof(writetmp)) { - writetmp[writeptr] = b; - ++writeptr; - } - ck ^= b; - send(0xFF); - } - // this should be checksum? - recv(); - uint8_t ck2 = cmd; - (void)ck2; // TODO: validate checksum - - send(0x2B); recv(); - send(term); -} else if (ch == 0x43) { - /* read data */ - uint8_t sz; - send(0xFF); recv(); sz = cmd; - send(0x2B); recv(); - -#ifdef DEBUG_MC_PROTOCOL - debug_printf("> %02X %02X\n", ch, sz); -#endif - - uint8_t ck = 0; - uint8_t b = 0xFF; - - for (int i = 0; i < sz; ++i) { - if (readptr == sizeof(readtmp.buf)) { - /* a game may read more than one 528-byte sector in a sequence of read ops, e.g. re4 */ - ++read_sector; - if (read_sector * 512 + 512 <= ps2_cardman_get_card_size()) { - ps2_dirty_lockout_renew(); - /* the spinlock will be unlocked by the DMA irq once all data is tx'd */ - ps2_dirty_lock(); - read_mc(read_sector * 512, &readtmp, 512+4); - // TODO: remove this if safe - // must make sure the dma completes for first byte before we start reading below - dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_RX_CHAN); - dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_TX_CHAN); - } - readptr = 0; - - eccptr = &readtmp.buf[512]; - memset(eccptr, 0, 16); - } - - if (readptr < sizeof(readtmp.buf)) { - b = readtmp.buf[readptr]; - send(b); - - if (readptr <= 512) { - uint8_t c = Table[b]; - eccptr[0] ^= c; - if (c & 0x80) { - eccptr[1] ^= ~(readptr & 0x7F); - eccptr[2] ^= (readptr & 0x7F); - } - - ++readptr; - - if ((readptr & 0x7F) == 0) { - eccptr[0] = ~eccptr[0]; - eccptr[0] &= 0x77; - - eccptr[1] = ~eccptr[1]; - eccptr[1] &= 0x7f; - - eccptr[2] = ~eccptr[2]; - eccptr[2] &= 0x7f; - - eccptr += 3; - } - } else { - ++readptr; - } - } else send(b); - ck ^= b; - recv(); - } - - send(ck); recv(); - send(term); -} else if (ch == 0x81) { - /* commit for read/write? */ - if (is_write) { - is_write = 0; - if (write_sector * 512 + 512 <= ps2_cardman_get_card_size()) { - ps2_dirty_lockout_renew(); - ps2_dirty_lock(); - write_mc(write_sector * 512, writetmp, 512); - ps2_dirty_mark(write_sector); - ps2_dirty_unlock(); -#ifdef DEBUG_MC_PROTOCOL - debug_printf("WR 0x%08X : %02X %02X .. %08X %08X %08X\n", - write_sector * 512, writetmp[0], writetmp[1], - *(uint32_t*)&writetmp[512], *(uint32_t*)&writetmp[516], *(uint32_t*)&writetmp[520]); -#endif - } - } else { -#ifdef DEBUG_MC_PROTOCOL - debug_printf("RD 0x%08X : %02X %02X .. %08X %08X %08X\n", - read_sector * 512, readtmp.buf[0], readtmp.buf[1], - *(uint32_t*)&readtmp.buf[512], *(uint32_t*)&readtmp.buf[516], *(uint32_t*)&readtmp.buf[520]); -#endif - } - - send(0x2B); recv(); - send(term); -} else if (ch == 0x82) { - /* do erase */ - if (erase_sector * 512 + 512 * ERASE_SECTORS <= ps2_cardman_get_card_size()) { - memset(readtmp.buf, 0xFF, 512); - ps2_dirty_lockout_renew(); - ps2_dirty_lock(); - for (int i = 0; i < ERASE_SECTORS; ++i) { - write_mc((erase_sector + i) * 512, readtmp.buf, 512); - ps2_dirty_mark(erase_sector + i); - } - ps2_dirty_unlock(); -#ifdef DEBUG_MC_PROTOCOL - debug_printf("ER 0x%08X\n", erase_sector * 512); -#endif - } - send(0x2B); recv(); - send(term); -} else if (ch == 0xBF) { - send(0xFF); recv(); - send(0x2B); recv(); - send(term); -} else if (ch == 0xF3) { - send(0xFF); recv(); - send(0x2B); recv(); - send(term); -} else if ((ch == 0xF7) && ps2_magicgate) { // TODO: it fails to get detected at all when ps2_magicgate==0, check if it's intentional - /* SIO_MEMCARD_KEY_SELECT */ - send(0xFF); recv(); - send(0x2B); recv(); - send(term); -} else if ((ch == 0xF0) && ps2_magicgate) { - /* auth stuff */ - send(0xFF); - recv(); - int subcmd = cmd; - if (subcmd == 0) { - /* probe support ? */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 1) { - debug_printf("iv : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(iv)); - - /* get IV */ - send(0x2B); recv(); - send(iv[7]); recv(); - send(iv[6]); recv(); - send(iv[5]); recv(); - send(iv[4]); recv(); - send(iv[3]); recv(); - send(iv[2]); recv(); - send(iv[1]); recv(); - send(iv[0]); recv(); - send(XOR8(iv)); recv(); - send(term); - } else if (subcmd == 2) { - debug_printf("seed : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(seed)); - - /* get seed */ - send(0x2B); recv(); - send(seed[7]); recv(); - send(seed[6]); recv(); - send(seed[5]); recv(); - send(seed[4]); recv(); - send(seed[3]); recv(); - send(seed[2]); recv(); - send(seed[1]); recv(); - send(seed[0]); recv(); - send(XOR8(seed)); recv(); - send(term); - } else if (subcmd == 3) { - /* dummy 3 */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 4) { - debug_printf("nonce : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(nonce)); - - /* get nonce */ - send(0x2B); recv(); - send(nonce[7]); recv(); - send(nonce[6]); recv(); - send(nonce[5]); recv(); - send(nonce[4]); recv(); - send(nonce[3]); recv(); - send(nonce[2]); recv(); - send(nonce[1]); recv(); - send(nonce[0]); recv(); - send(XOR8(nonce)); recv(); - send(term); - } else if (subcmd == 5) { - /* dummy 5 */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 6) { - /* MechaChallenge3 */ - send(0xFF); - recv(); MechaChallenge3[7] = cmd; - send(0xFF); - recv(); MechaChallenge3[6] = cmd; - send(0xFF); - recv(); MechaChallenge3[5] = cmd; - send(0xFF); - recv(); MechaChallenge3[4] = cmd; - send(0xFF); - recv(); MechaChallenge3[3] = cmd; - send(0xFF); - recv(); MechaChallenge3[2] = cmd; - send(0xFF); - recv(); MechaChallenge3[1] = cmd; - send(0xFF); - recv(); MechaChallenge3[0] = cmd; - /* TODO: checksum below */ - send(0xFF); recv(); - send(0x2B); recv(); - send(term); - - debug_printf("MechaChallenge3 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(MechaChallenge3)); - } else if (subcmd == 7) { - /* MechaChallenge2 */ - send(0xFF); - recv(); MechaChallenge2[7] = cmd; - send(0xFF); - recv(); MechaChallenge2[6] = cmd; - send(0xFF); - recv(); MechaChallenge2[5] = cmd; - send(0xFF); - recv(); MechaChallenge2[4] = cmd; - send(0xFF); - recv(); MechaChallenge2[3] = cmd; - send(0xFF); - recv(); MechaChallenge2[2] = cmd; - send(0xFF); - recv(); MechaChallenge2[1] = cmd; - send(0xFF); - recv(); MechaChallenge2[0] = cmd; - /* TODO: checksum below */ - send(0xFF); recv(); - send(0x2B); recv(); - send(term); - - debug_printf("MechaChallenge2 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(MechaChallenge2)); - } else if (subcmd == 8) { - /* dummy 8 */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 9) { - /* dummy 9 */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0xA) { - /* dummy A */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0xB) { - /* MechaChallenge1 */ - send(0xFF); - recv(); MechaChallenge1[7] = cmd; - send(0xFF); - recv(); MechaChallenge1[6] = cmd; - send(0xFF); - recv(); MechaChallenge1[5] = cmd; - send(0xFF); - recv(); MechaChallenge1[4] = cmd; - send(0xFF); - recv(); MechaChallenge1[3] = cmd; - send(0xFF); - recv(); MechaChallenge1[2] = cmd; - send(0xFF); - recv(); MechaChallenge1[1] = cmd; - send(0xFF); - recv(); MechaChallenge1[0] = cmd; - /* TODO: checksum below */ - send(0xFF); recv(); - send(0x2B); recv(); - send(term); - - debug_printf("MechaChallenge1 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(MechaChallenge1)); - } else if (subcmd == 0xC) { - /* dummy C */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0xD) { - /* dummy D */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0xE) { - /* dummy E */ - generateResponse(); - debug_printf("CardResponse1 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(CardResponse1)); - debug_printf("CardResponse2 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(CardResponse2)); - debug_printf("CardResponse3 : %02X %02X %02X %02X %02X %02X %02X %02X\n", ARG8(CardResponse3)); - send(0x2B); recv(); - send(term); - } else if (subcmd == 0xF) { - /* CardResponse1 */ - send(0x2B); recv(); - send(CardResponse1[7]); recv(); - send(CardResponse1[6]); recv(); - send(CardResponse1[5]); recv(); - send(CardResponse1[4]); recv(); - send(CardResponse1[3]); recv(); - send(CardResponse1[2]); recv(); - send(CardResponse1[1]); recv(); - send(CardResponse1[0]); recv(); - send(XOR8(CardResponse1)); recv(); - send(term); - } else if (subcmd == 0x10) { - /* dummy 10 */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0x11) { - /* CardResponse2 */ - send(0x2B); recv(); - send(CardResponse2[7]); recv(); - send(CardResponse2[6]); recv(); - send(CardResponse2[5]); recv(); - send(CardResponse2[4]); recv(); - send(CardResponse2[3]); recv(); - send(CardResponse2[2]); recv(); - send(CardResponse2[1]); recv(); - send(CardResponse2[0]); recv(); - send(XOR8(CardResponse2)); recv(); - send(term); - } else if (subcmd == 0x12) { - /* dummy 12 */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0x13) { - /* CardResponse3 */ - send(0x2B); recv(); - send(CardResponse3[7]); recv(); - send(CardResponse3[6]); recv(); - send(CardResponse3[5]); recv(); - send(CardResponse3[4]); recv(); - send(CardResponse3[3]); recv(); - send(CardResponse3[2]); recv(); - send(CardResponse3[1]); recv(); - send(CardResponse3[0]); recv(); - send(XOR8(CardResponse3)); recv(); - send(term); - } else if (subcmd == 0x14) { - /* dummy 14 */ - send(0x2B); recv(); - send(term); - } else { - debug_printf("unknown %02X -> %02X\n", ch, subcmd); - } -} else if ((ch == 0xF1 || ch == 0xF2) && ps2_magicgate) { - /* session key encrypt */ - send(0xFF); - recv(); - int subcmd = cmd; - if (subcmd == 0x50 || subcmd == 0x40) { - send(0x2B); recv(); - send(term); - } else if (subcmd == 0x51 || subcmd == 0x41) { - /* host sends key to us */ - for (size_t i = 0; i < sizeof(hostkey); ++i) { - send(0xFF); - recv(); - hostkey[i] = cmd; - } - send(0x2B); recv(); - send(term); - } else if (subcmd == 0x52 || subcmd == 0x42) { - /* now we encrypt/decrypt the key */ - send(0x2B); recv(); - send(term); - } else if (subcmd == 0x53 || subcmd == 0x43) { - send(0x2B); recv(); - /* we send key to the host */ - for (size_t i = 0; i < sizeof(hostkey); ++i) { - send(hostkey[i]); - recv(); - } - send(term); - } else { - debug_printf("!! unknown subcmd %02X -> %02X\n", 0xF2, subcmd); - } -} else { - debug_printf("!! unknown %02X\n", ch); -} - -#undef XOR8 diff --git a/src/sd.h b/src/sd.h index c18ab89..0985955 100644 --- a/src/sd.h +++ b/src/sd.h @@ -11,4 +11,7 @@ int sd_write(int fd, void *buf, size_t count); int sd_seek(int fd, uint64_t pos); int sd_filesize(int fd); int sd_mkdir(const char *path); -int sd_exists(const char *path); \ No newline at end of file +int sd_exists(const char *path); + +int sd_iterate_dir(int dir, int it); +size_t sd_get_name(int fd, char* name, size_t size); \ No newline at end of file