diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml deleted file mode 100644 index 7e7df583..00000000 --- a/.github/workflows/build-appimage.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: CMake Build (AppImage x86-64) - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - build: - - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v1 - - name: Install dependencies - run: | - sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list - sudo apt update - sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libqt5multimedia5-plugins qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades - - name: Create build environment - run: mkdir ${{runner.workspace}}/build - - name: Configure - working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE - - name: Make - working-directory: ${{runner.workspace}}/build - run: | - make -j$(nproc --all) - - name: Prepare AppDir for AppImage - working-directory: ${{runner.workspace}}/build - run: | - make install DESTDIR=AppDir - mv ./AppDir/usr/local/bin ./AppDir/usr/bin - mv ./AppDir/usr/local/share ./AppDir/usr/share - rm -rf ./AppDir/usr/local - - name: Prepare necessary Tools for building the AppImage - working-directory: ${{runner.workspace}}/build - run: | - wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage - wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage - chmod a+x linuxdeploy-x86_64.AppImage - chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage - - name: Build the AppImage - working-directory: ${{runner.workspace}}/build - run: | - ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage - mkdir dist - cp ./melonDS*.AppImage ./dist - - uses: actions/upload-artifact@v1 - with: - name: melonDS-appimage-x86_64 - path: ${{runner.workspace}}/build/dist diff --git a/.github/workflows/build-macos-universal.yml b/.github/workflows/build-macos-universal.yml deleted file mode 100644 index 4416ce7a..00000000 --- a/.github/workflows/build-macos-universal.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: CMake Build (macOS Universal) - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - prepare: - runs-on: [self-hosted, macOS, ARM64] - - steps: - - name: Clean workspace - run: rm -rf ${{runner.workspace}}/build - - - uses: actions/checkout@v3 - - - build-arm64: - needs: prepare - runs-on: [self-hosted, macOS, ARM64] - env: - homebrew_prefix: /opt/homebrew - - steps: - - name: Create build directory - run: mkdir -p ${{runner.workspace}}/build/arm64 - - - name: Configure - working-directory: ${{runner.workspace}}/build/arm64 - run: arch -arm64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON - - - name: Make - working-directory: ${{runner.workspace}}/build/arm64 - run: arch -arm64 make -j$(sysctl -n hw.logicalcpu) - - build-x86_64: - needs: prepare - runs-on: [self-hosted, macOS, ARM64] - env: - homebrew_prefix: /usr/local - - steps: - - name: Create build directory - run: mkdir -p ${{runner.workspace}}/build/x86_64 - - - name: Configure - working-directory: ${{runner.workspace}}/build/x86_64 - run: arch -x86_64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON - - - name: Make - working-directory: ${{runner.workspace}}/build/x86_64 - run: arch -x86_64 make -j$(sysctl -n hw.logicalcpu) - - universal-binary: - needs: [build-arm64, build-x86_64] - runs-on: [self-hosted, macOS, ARM64] - - steps: - - name: Merge binaries - run: $GITHUB_WORKSPACE/tools/mac-universal.py ${{runner.workspace}}/build/arm64/melonDS.app ${{runner.workspace}}/build/x86_64/melonDS.app ${{runner.workspace}}/build/universal/melonDS.app - - - name: Codesign app - run: codesign -s - --deep -f ${{runner.workspace}}/build/universal/melonDS.app - - - name: Create DMG - run: hdiutil create -fs HFS+ -volname melonDS -srcfolder ${{runner.workspace}}/build/universal/melonDS.app -ov -format UDBZ ${{runner.workspace}}/build/universal/melonDS.dmg - - - uses: actions/upload-artifact@v3 - with: - name: macOS-universal - path: ${{runner.workspace}}/build/universal/melonDS.dmg - diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 00000000..4178157d --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,85 @@ +name: macOS + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build-macos: + strategy: + matrix: + arch: [x86_64, arm64] + + name: ${{ matrix.arch }} + runs-on: macos-14 + steps: + - name: Check out sources + uses: actions/checkout@v3 + - name: Install dependencies for package building + run: | + brew install autoconf automake autoconf-archive libtool python-setuptools + - name: Set up CMake + uses: lukka/get-cmake@latest + - name: Set up vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db + - name: Build + uses: lukka/run-cmake@v10 + with: + configurePreset: release-mac-${{ matrix.arch }} + buildPreset: release-mac-${{ matrix.arch }} + - name: Compress app bundle + shell: bash + run: | + cd build/release-mac-${{ matrix.arch }} + zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: macOS-${{ matrix.arch }} + path: macOS-${{ matrix.arch }}.zip + retention-days: 1 + + universal-binary: + name: Universal binary + needs: [build-macos] + runs-on: macos-13 + continue-on-error: true + steps: + - name: Download x86_64 + uses: actions/download-artifact@v4 + with: + name: macOS-x86_64 + path: x86_64 + - name: Download arm64 + uses: actions/download-artifact@v4 + with: + name: macOS-arm64 + path: arm64 + - name: Combine app bundles + shell: bash + run: | + unzip x86_64/*.zip -d x86_64 + unzip arm64/*.zip -d arm64 + lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS + cp -a arm64/melonDS.app melonDS.app + cp melonDS melonDS.app/Contents/MacOS/melonDS + codesign -s - --deep melonDS.app + zip -r -y macOS-universal.zip melonDS.app + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: macOS-universal + path: macOS-universal.zip +# - name: Clean up architecture-specific artifacts +# uses: geekyeggo/delete-artifact@v4 +# with: +# failOnError: false +# name: | +# macOS-x86_64 +# macOS-arm64 diff --git a/.github/workflows/build-ubuntu-aarch64.yml b/.github/workflows/build-ubuntu-aarch64.yml deleted file mode 100644 index 096bc0b9..00000000 --- a/.github/workflows/build-ubuntu-aarch64.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: CMake Build (Ubuntu aarch64) - -on: - push: - branches: - - master - pull_request: - branches: - - master - -env: - BUILD_TYPE: Release - -jobs: - build: - runs-on: ubuntu-20.04 - container: ubuntu:20.04 - - steps: - - name: Prepare system - shell: bash - run: | - apt update - apt -y full-upgrade - apt -y install git - - name: Check out source - uses: actions/checkout@v1 - - name: Install dependencies - shell: bash - run: | - dpkg --add-architecture arm64 - sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new" - rm /etc/apt/sources.list - mv /etc/apt/sources.list{.new,} - apt update - DEBIAN_FRONTEND=noninteractive apt install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu {libsdl2,qtbase5,qtbase5-private,qtmultimedia5,libslirp,libarchive,libzstd}-dev:arm64 zstd:arm64 cmake extra-cmake-modules dpkg-dev - - name: Configure - shell: bash - run: | - CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -B build - - name: Make - shell: bash - run: | - cmake --build build -j$(nproc --all) - mkdir dist - cp build/melonDS dist - - uses: actions/upload-artifact@v1 - with: - name: melonDS-ubuntu-aarch64 - path: dist diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index b6c50e9a..e96f98aa 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -1,4 +1,4 @@ -name: CMake Build (Ubuntu x86-64) +name: Ubuntu on: push: @@ -9,29 +9,77 @@ on: - master jobs: - build: - - runs-on: ubuntu-20.04 + build-x86_64: + name: x86_64 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 + name: Check out sources - name: Install dependencies run: | sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list sudo apt update - sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades - - name: Create build environment - run: mkdir ${{runner.workspace}}/build + sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev \ + qt6-{base,base-private,multimedia}-dev libslirp0 libslirp-dev libarchive-dev libzstd-dev libfuse2 - name: Configure - working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE - - name: Make - working-directory: ${{runner.workspace}}/build + run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr + - name: Build run: | - make -j$(nproc --all) - mkdir dist - cp melonDS dist - - uses: actions/upload-artifact@v1 + cmake --build build + DESTDIR=AppDir cmake --install build + - uses: actions/upload-artifact@v4 with: name: melonDS-ubuntu-x86_64 - path: ${{runner.workspace}}/build/dist + path: AppDir/usr/bin/melonDS + - name: Fetch AppImage tools + run: | + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + chmod a+x linuxdeploy-*.AppImage + - name: Build the AppImage + env: + QMAKE: /usr/lib/qt6/bin/qmake + run: | + ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage + - uses: actions/upload-artifact@v4 + with: + name: melonDS-appimage-x86_64 + path: melonDS*.AppImage + + build-aarch64: + name: aarch64 + runs-on: ubuntu-latest + container: ubuntu:22.04 + + steps: + - name: Prepare system + shell: bash + run: | + dpkg --add-architecture arm64 + sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new" + rm /etc/apt/sources.list + mv /etc/apt/sources.list{.new,} + apt update + apt -y full-upgrade + apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \ + {libsdl2,qt6-{base,base-private,multimedia},libslirp,libarchive,libzstd}-dev:arm64 \ + pkg-config dpkg-dev + - name: Check out source + uses: actions/checkout@v4 + - name: Configure + shell: bash + run: | + cmake -B build -G Ninja \ + -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \ + -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \ + -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \ + -DUSE_QT6=ON + - name: Build + shell: bash + run: | + cmake --build build + - uses: actions/upload-artifact@v4 + with: + name: melonDS-ubuntu-aarch64 + path: build/melonDS diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 70b11c05..e6846da7 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1,4 +1,4 @@ -name: CMake Build (Windows x86-64) +name: Windows on: push: diff --git a/.gitignore b/.gitignore index e76a0944..d7001e4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -build +build*/ bin obj *.depend @@ -7,7 +7,7 @@ obj melon_grc.c melon_grc.h melon.rc -cmake-build +cmake-build* cmake-build-debug compile_commands.json .idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d82eff..57400e36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.16) cmake_policy(VERSION 3.15) if (POLICY CMP0076) @@ -8,6 +8,12 @@ endif() set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake") + +option(USE_VCPKG "Use vcpkg for dependency packages" OFF) +if (USE_VCPKG) + include(ConfigureVcpkg) +endif() project(melonDS VERSION 0.9.5 @@ -28,8 +34,6 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -add_compile_definitions(MELONDS_VERSION="${melonDS_VERSION}") - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") @@ -73,11 +77,6 @@ if (ENABLE_LTO) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") -string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") -string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - if (NOT APPLE) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s") endif() @@ -99,6 +98,11 @@ if (CCACHE) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) endif() +option(ENABLE_GDBSTUB "Enable GDB stub" ON) +if (ENABLE_GDBSTUB) + add_definitions(-DGDBSTUB_ENABLED) +endif() + option(BUILD_QT_SDL "Build Qt/SDL frontend" ON) add_subdirectory(src) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..e14eda24 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,88 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "release", + "displayName": "Release", + "description": "Default release build configuration.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release" + }, + { + "inherits": "release", + "name": "release-vcpkg", + "displayName": "Release (vcpkg)", + "description": "Release build with packages from vcpkg.", + "cacheVariables": { + "USE_VCPKG": { + "type": "BOOL", + "value": "ON" + } + } + }, + { + "name": "release-mac-x86_64", + "inherits": "release-vcpkg", + "displayName": "macOS release (x86_64)", + "binaryDir": "${sourceDir}/build/release-mac-x86_64", + "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" } + }, + { + "name": "release-mac-arm64", + "inherits": "release-vcpkg", + "displayName": "macOS release (arm64)", + "binaryDir": "${sourceDir}/build/release-mac-arm64", + "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" } + } + ], + "buildPresets": [ + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "release-vcpkg", + "configurePreset": "release-vcpkg" + }, + { + "name": "release-mac-x86_64", + "configurePreset": "release-mac-x86_64" + }, + { + "name": "release-mac-arm64", + "configurePreset": "release-mac-arm64" + } + ], + "workflowPresets": [ + { + "name": "release", + "displayName": "Release", + "steps": [ + { "type": "configure", "name": "release" }, + { "type": "build", "name": "release" } + ] + }, + { + "name": "release-vcpkg", + "displayName": "Release (vcpkg)", + "steps": [ + { "type": "configure", "name": "release-vcpkg" }, + { "type": "build", "name": "release-vcpkg" } + ] + }, + { + "name": "release-mac-x86_64", + "steps": [ + { "type": "configure", "name": "release-mac-x86_64" }, + { "type": "build", "name": "release-mac-x86_64" } + ] + }, + { + "name": "release-mac-arm64", + "steps": [ + { "type": "configure", "name": "release-mac-arm64" }, + { "type": "build", "name": "release-mac-arm64" } + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index de494305..2cef7983 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,9 @@
- - - - + + +

DS emulator, sorta diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake new file mode 100644 index 00000000..c9f3e92f --- /dev/null +++ b/cmake/ConfigureVcpkg.cmake @@ -0,0 +1,91 @@ +include(FetchContent) + +set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg") +set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository") + +if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") + file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE) + FetchContent_Declare(vcpkg + GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" + GIT_TAG 2024.01.12 + SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") + FetchContent_MakeAvailable(vcpkg) +endif() + +set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets") + +option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON) + +if (CMAKE_OSX_ARCHITECTURES MATCHES ";") + message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.") +endif() + +if (USE_RECOMMENDED_TRIPLETS) + execute_process( + COMMAND uname -m + OUTPUT_VARIABLE _HOST_PROCESSOR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(_CAN_TARGET_AS_HOST OFF) + + if (APPLE) + if (NOT CMAKE_OSX_ARCHITECTURES) + if (_HOST_PROCESSOR STREQUAL arm64) + set(CMAKE_OSX_ARCHITECTURES arm64) + else() + set(CMAKE_OSX_ARCHITECTURES x86_64) + endif() + endif() + + if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64) + set(_WANTED_TRIPLET arm64-osx-11-release) + set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0) + else() + set(_WANTED_TRIPLET x64-osx-1015-release) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15) + endif() + elseif(WIN32) + # TODO Windows arm64 if possible + set(_CAN_TARGET_AS_HOST ON) + set(_WANTED_TRIPLET x64-mingw-static) + endif() + + # Don't override it if the user set something else + if (NOT VCPKG_TARGET_TRIPLET) + set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}") + else() + set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}") + endif() + + if (APPLE) + if (_HOST_PROCESSOR MATCHES arm64) + if (_WANTED_TRIPLET MATCHES "^arm64-osx-") + set(_CAN_TARGET_AS_HOST ON) + elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release") + # Use the default triplet for when building for arm64 + # because we're probably making a universal build + set(VCPKG_HOST_TRIPLET arm64-osx-11-release) + endif() + else() + if (_WANTED_TRIPLET MATCHES "^x64-osx-") + set(_CAN_TARGET_AS_HOST ON) + elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release") + set(VCPKG_HOST_TRIPLET x64-osx-1015-release) + endif() + endif() + endif() + + # If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries. + # In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant. + if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET) + option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON) + else() + option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF) + endif() + + if (VCPKG_TARGET_AS_HOST) + set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg") + endif() +endif() + +set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") diff --git a/cmake/DefaultBuildFlags.cmake b/cmake/DefaultBuildFlags.cmake new file mode 100644 index 00000000..683767b3 --- /dev/null +++ b/cmake/DefaultBuildFlags.cmake @@ -0,0 +1,9 @@ +if (CMAKE_C_COMPILER_ID STREQUAL GNU) + set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og") +endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) + set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og") +endif() + +string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}") +string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}") diff --git a/cmake/FixInterfaceIncludes.cmake b/cmake/FixInterfaceIncludes.cmake index 513c1117..5c285d7a 100644 --- a/cmake/FixInterfaceIncludes.cmake +++ b/cmake/FixInterfaceIncludes.cmake @@ -19,6 +19,13 @@ function(fix_interface_includes) if (PARENT_DIR MATCHES "include$") list(APPEND NEW_DIRS "${PARENT_DIR}") endif() + + # HACK + # The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its + # include directory and CMake doesn't like that. + if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive) + list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*") + endif() endforeach() list(APPEND DIRS ${NEW_DIRS}) diff --git a/cmake/overlay-triplets/arm64-osx-11-release.cmake b/cmake/overlay-triplets/arm64-osx-11-release.cmake new file mode 100644 index 00000000..7c4b43ae --- /dev/null +++ b/cmake/overlay-triplets/arm64-osx-11-release.cmake @@ -0,0 +1,12 @@ +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_CMAKE_SYSTEM_VERSION 11.0) +set(VCPKG_OSX_ARCHITECTURES arm64) +set(VCPKG_BUILD_TYPE release) +set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0) + +set(VCPKG_C_FLAGS -mmacosx-version-min=11.0) +set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0) diff --git a/cmake/overlay-triplets/x64-osx-1015-release.cmake b/cmake/overlay-triplets/x64-osx-1015-release.cmake new file mode 100644 index 00000000..fcb67a7a --- /dev/null +++ b/cmake/overlay-triplets/x64-osx-1015-release.cmake @@ -0,0 +1,12 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_CMAKE_SYSTEM_VERSION 10.15) +set(VCPKG_OSX_ARCHITECTURES x86_64) +set(VCPKG_BUILD_TYPE release) +set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15) + +set(VCPKG_C_FLAGS -mmacosx-version-min=10.15) +set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15) diff --git a/freebios/bios_common.S b/freebios/bios_common.S index 56d349ef..308d67c4 100755 --- a/freebios/bios_common.S +++ b/freebios/bios_common.S @@ -517,6 +517,9 @@ swi_get_crc16: mov const_0x1E, #0x1E adr crc_table_ptr, crc_table + bic crc_value, crc_value, #0xFF000000 + bic crc_value, crc_value, #0x00FF0000 + movs crc_length, crc_length, lsr #1 beq 1f diff --git a/freebios/drastic_bios_arm7.bin b/freebios/drastic_bios_arm7.bin index e586eb9f..adffb53a 100755 Binary files a/freebios/drastic_bios_arm7.bin and b/freebios/drastic_bios_arm7.bin differ diff --git a/freebios/drastic_bios_arm9.bin b/freebios/drastic_bios_arm9.bin index 51a82829..95c330d0 100755 Binary files a/freebios/drastic_bios_arm9.bin and b/freebios/drastic_bios_arm9.bin differ diff --git a/res/melon.rc.in b/res/melon.rc.in index 3f2d8e4f..eb437b32 100644 --- a/res/melon.rc.in +++ b/res/melon.rc.in @@ -18,7 +18,7 @@ FILETYPE VFT_APP VALUE "FileVersion", "${melonDS_VERSION}" VALUE "FileDescription", "melonDS emulator" VALUE "InternalName", "SDnolem" - VALUE "LegalCopyright", "2016-2022 melonDS team" + VALUE "LegalCopyright", "2016-2023 melonDS team" VALUE "LegalTrademarks", "" VALUE "OriginalFilename", "melonDS.exe" VALUE "ProductName", "melonDS" diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp index d1f34fb6..602a2e7b 100644 --- a/src/ARCodeFile.cpp +++ b/src/ARCodeFile.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,6 +21,8 @@ #include "ARCodeFile.h" #include "Platform.h" +namespace melonDS +{ using namespace Platform; // TODO: import codes from other sources (usrcheat.dat, ...) @@ -162,23 +164,25 @@ bool ARCodeFile::Save() { ARCodeCat& cat = *it; - if (it != Categories.begin()) FileWriteFormatted(f, "\r\n"); - FileWriteFormatted(f, "CAT %s\r\n\r\n", cat.Name.c_str()); + if (it != Categories.begin()) FileWriteFormatted(f, "\n"); + FileWriteFormatted(f, "CAT %s\n\n", cat.Name.c_str()); for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++) { ARCode& code = *jt; - FileWriteFormatted(f, "CODE %d %s\r\n", code.Enabled, code.Name.c_str()); + FileWriteFormatted(f, "CODE %d %s\n", code.Enabled, code.Name.c_str()); for (size_t i = 0; i < code.Code.size(); i+=2) { - FileWriteFormatted(f, "%08X %08X\r\n", code.Code[i], code.Code[i + 1]); + FileWriteFormatted(f, "%08X %08X\n", code.Code[i], code.Code[i + 1]); } - FileWriteFormatted(f, "\r\n"); + FileWriteFormatted(f, "\n"); } } CloseFile(f); return true; } + +} \ No newline at end of file diff --git a/src/ARCodeFile.h b/src/ARCodeFile.h index d0e82550..11e71efe 100644 --- a/src/ARCodeFile.h +++ b/src/ARCodeFile.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,6 +24,8 @@ #include #include "types.h" +namespace melonDS +{ struct ARCode { std::string Name; @@ -59,4 +61,5 @@ private: std::string Filename; }; +} #endif // ARCODEFILE_H diff --git a/src/AREngine.cpp b/src/AREngine.cpp index 62600f9e..c7d49fe6 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -23,84 +23,33 @@ #include "AREngine.h" #include "Platform.h" +namespace melonDS +{ + using Platform::Log; using Platform::LogLevel; -namespace AREngine -{ - -// AR code file - frontend is responsible for managing this -ARCodeFile* CodeFile; - -u8 (*BusRead8)(u32 addr); -u16 (*BusRead16)(u32 addr); -u32 (*BusRead32)(u32 addr); -void (*BusWrite8)(u32 addr, u8 val); -void (*BusWrite16)(u32 addr, u16 val); -void (*BusWrite32)(u32 addr, u32 val); - - -bool Init() +AREngine::AREngine(melonDS::NDS& nds) : NDS(nds) { CodeFile = nullptr; - - return true; } -void DeInit() -{ -} - -void Reset() -{ - if (NDS::ConsoleType == 1) - { - BusRead8 = DSi::ARM7Read8; - BusRead16 = DSi::ARM7Read16; - BusRead32 = DSi::ARM7Read32; - BusWrite8 = DSi::ARM7Write8; - BusWrite16 = DSi::ARM7Write16; - BusWrite32 = DSi::ARM7Write32; - } - else - { - BusRead8 = NDS::ARM7Read8; - BusRead16 = NDS::ARM7Read16; - BusRead32 = NDS::ARM7Read32; - BusWrite8 = NDS::ARM7Write8; - BusWrite16 = NDS::ARM7Write16; - BusWrite32 = NDS::ARM7Write32; - } -} - - -ARCodeFile* GetCodeFile() -{ - return CodeFile; -} - -void SetCodeFile(ARCodeFile* file) -{ - CodeFile = file; -} - - #define case16(x) \ case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \ case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \ case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \ case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F) -void RunCheat(ARCode& arcode) +void AREngine::RunCheat(const ARCode& arcode) { - u32* code = &arcode.Code[0]; + const u32* code = &arcode.Code[0]; u32 offset = 0; u32 datareg = 0; u32 cond = 1; u32 condstack = 0; - u32* loopstart = code; + const u32* loopstart = code; u32 loopcount = 0; u32 loopcond = 1; u32 loopcondstack = 0; @@ -135,15 +84,15 @@ void RunCheat(ARCode& arcode) switch (op) { case16(0x00): // 32-bit write - BusWrite32((a & 0x0FFFFFFF) + offset, b); + NDS.ARM7Write32((a & 0x0FFFFFFF) + offset, b); break; case16(0x10): // 16-bit write - BusWrite16((a & 0x0FFFFFFF) + offset, b & 0xFFFF); + NDS.ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF); break; case16(0x20): // 8-bit write - BusWrite8((a & 0x0FFFFFFF) + offset, b & 0xFF); + NDS.ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF); break; case16(0x30): // IF b > u32[a] @@ -153,7 +102,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b > chk) ? 1:0; } @@ -166,7 +115,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b < chk) ? 1:0; } @@ -179,7 +128,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b == chk) ? 1:0; } @@ -192,7 +141,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b != chk) ? 1:0; } @@ -205,7 +154,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -220,7 +169,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -235,7 +184,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -250,7 +199,7 @@ void RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -259,7 +208,7 @@ void RunCheat(ARCode& arcode) break; case16(0xB0): // offset = u32[a + offset] - offset = BusRead32((a & 0x0FFFFFFF) + offset); + offset = NDS.ARM7Read32((a & 0x0FFFFFFF) + offset); break; case 0xC0: // FOR 0..b @@ -296,7 +245,7 @@ void RunCheat(ARCode& arcode) break; case 0xC6: // u32[b] = offset - BusWrite32(b, offset); + NDS.ARM7Write32(b, offset); break; case 0xD0: // ENDIF @@ -345,30 +294,30 @@ void RunCheat(ARCode& arcode) break; case 0xD6: // u32[b+offset] = datareg / offset += 4 - BusWrite32(b + offset, datareg); + NDS.ARM7Write32(b + offset, datareg); offset += 4; break; case 0xD7: // u16[b+offset] = datareg / offset += 2 - BusWrite16(b + offset, datareg & 0xFFFF); + NDS.ARM7Write16(b + offset, datareg & 0xFFFF); offset += 2; break; case 0xD8: // u8[b+offset] = datareg / offset += 1 - BusWrite8(b + offset, datareg & 0xFF); + NDS.ARM7Write8(b + offset, datareg & 0xFF); offset += 1; break; case 0xD9: // datareg = u32[b+offset] - datareg = BusRead32(b + offset); + datareg = NDS.ARM7Read32(b + offset); break; case 0xDA: // datareg = u16[b+offset] - datareg = BusRead16(b + offset); + datareg = NDS.ARM7Read16(b + offset); break; case 0xDB: // datareg = u8[b+offset] - datareg = BusRead8(b + offset); + datareg = NDS.ARM7Read8(b + offset); break; case 0xDC: // offset += b @@ -383,8 +332,8 @@ void RunCheat(ARCode& arcode) u32 bytesleft = b; while (bytesleft >= 8) { - BusWrite32(dstaddr, *code++); dstaddr += 4; - BusWrite32(dstaddr, *code++); dstaddr += 4; + NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4; + NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4; bytesleft -= 8; } if (bytesleft > 0) @@ -393,13 +342,13 @@ void RunCheat(ARCode& arcode) code += 2; if (bytesleft >= 4) { - BusWrite32(dstaddr, *(u32*)leftover); dstaddr += 4; + NDS.ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4; leftover += 4; bytesleft -= 4; } while (bytesleft > 0) { - BusWrite8(dstaddr, *leftover++); dstaddr++; + NDS.ARM7Write8(dstaddr, *leftover++); dstaddr++; bytesleft--; } } @@ -415,14 +364,14 @@ void RunCheat(ARCode& arcode) u32 bytesleft = b; while (bytesleft >= 4) { - BusWrite32(dstaddr, BusRead32(srcaddr)); + NDS.ARM7Write32(dstaddr, NDS.ARM7Read32(srcaddr)); srcaddr += 4; dstaddr += 4; bytesleft -= 4; } while (bytesleft > 0) { - BusWrite8(dstaddr, BusRead8(srcaddr)); + NDS.ARM7Write8(dstaddr, NDS.ARM7Read8(srcaddr)); srcaddr++; dstaddr++; bytesleft--; @@ -437,7 +386,7 @@ void RunCheat(ARCode& arcode) } } -void RunCheats() +void AREngine::RunCheats() { if (!CodeFile) return; @@ -454,5 +403,4 @@ void RunCheats() } } } - } diff --git a/src/AREngine.h b/src/AREngine.h index 3e6e9dd4..21044676 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,18 +21,23 @@ #include "ARCodeFile.h" -namespace AREngine +namespace melonDS { +class NDS; +class AREngine +{ +public: + AREngine(melonDS::NDS& nds); -bool Init(); -void DeInit(); -void Reset(); + ARCodeFile* GetCodeFile() { return CodeFile; } + void SetCodeFile(ARCodeFile* file) { CodeFile = file; } -ARCodeFile* GetCodeFile(); -void SetCodeFile(ARCodeFile* file); - -void RunCheats(); + void RunCheats(); + void RunCheat(const ARCode& arcode); +private: + melonDS::NDS& NDS; + ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this +}; } - #endif // ARENGINE_H diff --git a/src/ARM.cpp b/src/ARM.cpp index b59530d1..c2f6a6c2 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -17,6 +17,7 @@ */ #include +#include #include #include "NDS.h" #include "DSi.h" @@ -25,15 +26,53 @@ #include "AREngine.h" #include "ARMJIT.h" #include "Platform.h" - -#ifdef JIT_ENABLED -#include "ARMJIT.h" +#include "GPU.h" #include "ARMJIT_Memory.h" -#endif +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; +#ifdef GDBSTUB_ENABLED +void ARM::GdbCheckA() +{ + if (!IsSingleStep && !BreakReq) + { // check if eg. break signal is incoming etc. + Gdb::StubState st = GdbStub.Enter(false, Gdb::TgtStatus::NoEvent, ~(u32)0u, BreakOnStartup); + BreakOnStartup = false; + IsSingleStep = st == Gdb::StubState::Step; + BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break; + } +} +void ARM::GdbCheckB() +{ + if (IsSingleStep || BreakReq) + { // use else here or we single-step the same insn twice in gdb + u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4); + Gdb::StubState st = GdbStub.Enter(true, Gdb::TgtStatus::SingleStep, pc_real); + IsSingleStep = st == Gdb::StubState::Step; + BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break; + } +} +void ARM::GdbCheckC() +{ + u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4); + Gdb::StubState st = GdbStub.CheckBkpt(pc_real, true, true); + if (st != Gdb::StubState::CheckNoHit) + { + IsSingleStep = st == Gdb::StubState::Step; + BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break; + } + else GdbCheckB(); +} +#else +void ARM::GdbCheckA() {} +void ARM::GdbCheckB() {} +void ARM::GdbCheckC() {} +#endif + + // instruction timing notes // // * simple instruction: 1S (code) @@ -48,7 +87,7 @@ using Platform::LogLevel; -u32 ARM::ConditionTable[16] = +const u32 ARM::ConditionTable[16] = { 0xF0F0, // EQ 0x0F0F, // NE @@ -68,11 +107,22 @@ u32 ARM::ConditionTable[16] = 0x0000 // NE }; - -ARM::ARM(u32 num) +ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) : +#ifdef GDBSTUB_ENABLED + GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0), +#endif + Num(num), // well uh + NDS(nds) { - // well uh - Num = num; +#ifdef GDBSTUB_ENABLED + if (gdb +#ifdef JIT_ENABLED + && !jit // TODO: Should we support toggling the GdbStub without destroying the ARM? +#endif + ) + GdbStub.Init(); + IsSingleStep = false; +#endif } ARM::~ARM() @@ -80,25 +130,21 @@ ARM::~ARM() // dorp } -ARMv5::ARMv5() : ARM(0) +ARMv5::ARMv5(melonDS::NDS& nds, std::optional gdb, bool jit) : ARM(0, jit, gdb, nds) { -#ifndef JIT_ENABLED - DTCM = new u8[DTCMPhysicalSize]; -#endif + DTCM = NDS.JIT.Memory.GetARM9DTCM(); PU_Map = PU_PrivMap; } -ARMv4::ARMv4() : ARM(1) +ARMv4::ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit) : ARM(1, jit, gdb, nds) { // } ARMv5::~ARMv5() { -#ifndef JIT_ENABLED - delete[] DTCM; -#endif + // DTCM is owned by Memory, not going to delete it } void ARM::Reset() @@ -139,62 +185,22 @@ void ARM::Reset() FastBlockLookupSize = 0; #endif +#ifdef GDBSTUB_ENABLED + IsSingleStep = false; + BreakReq = false; +#endif + // zorp JumpTo(ExceptionBase); } void ARMv5::Reset() { - if (NDS::ConsoleType == 1) - { - BusRead8 = DSi::ARM9Read8; - BusRead16 = DSi::ARM9Read16; - BusRead32 = DSi::ARM9Read32; - BusWrite8 = DSi::ARM9Write8; - BusWrite16 = DSi::ARM9Write16; - BusWrite32 = DSi::ARM9Write32; - GetMemRegion = DSi::ARM9GetMemRegion; - } - else - { - BusRead8 = NDS::ARM9Read8; - BusRead16 = NDS::ARM9Read16; - BusRead32 = NDS::ARM9Read32; - BusWrite8 = NDS::ARM9Write8; - BusWrite16 = NDS::ARM9Write16; - BusWrite32 = NDS::ARM9Write32; - GetMemRegion = NDS::ARM9GetMemRegion; - } - PU_Map = PU_PrivMap; ARM::Reset(); } -void ARMv4::Reset() -{ - if (NDS::ConsoleType) - { - BusRead8 = DSi::ARM7Read8; - BusRead16 = DSi::ARM7Read16; - BusRead32 = DSi::ARM7Read32; - BusWrite8 = DSi::ARM7Write8; - BusWrite16 = DSi::ARM7Write16; - BusWrite32 = DSi::ARM7Write32; - } - else - { - BusRead8 = NDS::ARM7Read8; - BusRead16 = NDS::ARM7Read16; - BusRead32 = NDS::ARM7Read32; - BusWrite8 = NDS::ARM7Write8; - BusWrite16 = NDS::ARM7Write16; - BusWrite32 = NDS::ARM7Write32; - } - - ARM::Reset(); -} - void ARM::DoSavestate(Savestate* file) { @@ -217,7 +223,7 @@ void ARM::DoSavestate(Savestate* file) file->VarArray(R_UND, 3*sizeof(u32)); file->Var32(&CurInstr); #ifdef JIT_ENABLED - if (file->Saving && NDS::EnableJIT) + if (file->Saving && NDS.IsJITEnabled()) { // hack, the JIT doesn't really pipeline // but we still want JIT save states to be @@ -343,7 +349,7 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr) return; } - NDS::MonitorARM9Jump(addr); + NDS.MonitorARM9Jump(addr); } void ARMv4::JumpTo(u32 addr, bool restorecpsr) @@ -371,7 +377,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) NextInstr[0] = CodeRead16(addr); NextInstr[1] = CodeRead16(addr+2); - Cycles += NDS::ARM7MemTimings[CodeCycles][0] + NDS::ARM7MemTimings[CodeCycles][1]; + Cycles += NDS.ARM7MemTimings[CodeCycles][0] + NDS.ARM7MemTimings[CodeCycles][1]; CPSR |= 0x20; } @@ -384,7 +390,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) NextInstr[0] = CodeRead32(addr); NextInstr[1] = CodeRead32(addr+4); - Cycles += NDS::ARM7MemTimings[CodeCycles][2] + NDS::ARM7MemTimings[CodeCycles][3]; + Cycles += NDS.ARM7MemTimings[CodeCycles][2] + NDS.ARM7MemTimings[CodeCycles][3]; CPSR &= ~0x20; } @@ -529,8 +535,8 @@ void ARM::TriggerIRQ() // normally, those work by hijacking the ARM7 VBlank handler if (Num == 1) { - if ((NDS::IF[1] & NDS::IE[1]) & (1<>12] & 0x04)) { Log(LogLevel::Error, "!!!!! EXCEPTION REGION NOT EXECUTABLE. THIS IS VERY BAD!!\n"); - NDS::Stop(Platform::StopReason::BadExceptionRegion); + NDS.Stop(Platform::StopReason::BadExceptionRegion); return; } @@ -571,31 +577,40 @@ void ARMv5::DataAbort() JumpTo(ExceptionBase + 0x10); } +void ARM::CheckGdbIncoming() +{ + GdbCheckA(); +} + void ARMv5::Execute() { + GdbCheckB(); + if (Halted) { if (Halted == 2) { Halted = 0; } - else if (NDS::HaltInterrupted(0)) + else if (NDS.HaltInterrupted(0)) { Halted = 0; - if (NDS::IME[0] & 0x1) + if (NDS.IME[0] & 0x1) TriggerIRQ(); } else { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; return; } } - while (NDS::ARM9Timestamp < NDS::ARM9Target) + while (NDS.ARM9Timestamp < NDS.ARM9Target) { if (CPSR & 0x20) // THUMB { + GdbCheckC(); + // prefetch R[15] += 2; CurInstr = NextInstr[0]; @@ -609,6 +624,8 @@ void ARMv5::Execute() } else { + GdbCheckC(); + // prefetch R[15] += 4; CurInstr = NextInstr[0]; @@ -632,9 +649,9 @@ void ARMv5::Execute() // TODO optimize this shit!!! if (Halted) { - if (Halted == 1 && NDS::ARM9Timestamp < NDS::ARM9Target) + if (Halted == 1 && NDS.ARM9Timestamp < NDS.ARM9Target) { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; } break; } @@ -645,7 +662,7 @@ void ARMv5::Execute() }*/ if (IRQ) TriggerIRQ(); - NDS::ARM9Timestamp += Cycles; + NDS.ARM9Timestamp += Cycles; Cycles = 0; } @@ -662,37 +679,37 @@ void ARMv5::ExecuteJIT() { Halted = 0; } - else if (NDS::HaltInterrupted(0)) + else if (NDS.HaltInterrupted(0)) { Halted = 0; - if (NDS::IME[0] & 0x1) + if (NDS.IME[0] & 0x1) TriggerIRQ(); } else { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; return; } } - while (NDS::ARM9Timestamp < NDS::ARM9Target) + while (NDS.ARM9Timestamp < NDS.ARM9Target) { u32 instrAddr = R[15] - ((CPSR&0x20)?2:4); if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize)) - && !ARMJIT::SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) + && !NDS.JIT.SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; Log(LogLevel::Error, "ARMv5 PC in non executable region %08X\n", R[15]); return; } - ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(0, FastBlockLookup, + JitBlockEntry block = NDS.JIT.LookUpBlock(0, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); else - ARMJIT::CompileBlock(this); + NDS.JIT.CompileBlock(this); if (StopExecution) { @@ -702,17 +719,17 @@ void ARMv5::ExecuteJIT() if (Halted || IdleLoop) { - if ((Halted == 1 || IdleLoop) && NDS::ARM9Timestamp < NDS::ARM9Target) + if ((Halted == 1 || IdleLoop) && NDS.ARM9Timestamp < NDS.ARM9Target) { Cycles = 0; - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; } IdleLoop = 0; break; } } - NDS::ARM9Timestamp += Cycles; + NDS.ARM9Timestamp += Cycles; Cycles = 0; } @@ -723,29 +740,33 @@ void ARMv5::ExecuteJIT() void ARMv4::Execute() { + GdbCheckB(); + if (Halted) { if (Halted == 2) { Halted = 0; } - else if (NDS::HaltInterrupted(1)) + else if (NDS.HaltInterrupted(1)) { Halted = 0; - if (NDS::IME[1] & 0x1) + if (NDS.IME[1] & 0x1) TriggerIRQ(); } else { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; return; } } - while (NDS::ARM7Timestamp < NDS::ARM7Target) + while (NDS.ARM7Timestamp < NDS.ARM7Target) { if (CPSR & 0x20) // THUMB { + GdbCheckC(); + // prefetch R[15] += 2; CurInstr = NextInstr[0]; @@ -758,6 +779,8 @@ void ARMv4::Execute() } else { + GdbCheckC(); + // prefetch R[15] += 4; CurInstr = NextInstr[0]; @@ -777,9 +800,9 @@ void ARMv4::Execute() // TODO optimize this shit!!! if (Halted) { - if (Halted == 1 && NDS::ARM7Timestamp < NDS::ARM7Target) + if (Halted == 1 && NDS.ARM7Timestamp < NDS.ARM7Target) { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; } break; } @@ -790,7 +813,7 @@ void ARMv4::Execute() }*/ if (IRQ) TriggerIRQ(); - NDS::ARM7Timestamp += Cycles; + NDS.ARM7Timestamp += Cycles; Cycles = 0; } @@ -799,7 +822,9 @@ void ARMv4::Execute() if (Halted == 4) { - DSi::SoftReset(); + assert(NDS.ConsoleType == 1); + auto& dsi = dynamic_cast(NDS); + dsi.SoftReset(); Halted = 2; } } @@ -813,37 +838,37 @@ void ARMv4::ExecuteJIT() { Halted = 0; } - else if (NDS::HaltInterrupted(1)) + else if (NDS.HaltInterrupted(1)) { Halted = 0; - if (NDS::IME[1] & 0x1) + if (NDS.IME[1] & 0x1) TriggerIRQ(); } else { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; return; } } - while (NDS::ARM7Timestamp < NDS::ARM7Target) + while (NDS.ARM7Timestamp < NDS.ARM7Target) { u32 instrAddr = R[15] - ((CPSR&0x20)?2:4); if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize)) - && !ARMJIT::SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) + && !NDS.JIT.SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; Log(LogLevel::Error, "ARMv4 PC in non executable region %08X\n", R[15]); return; } - ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(1, FastBlockLookup, + JitBlockEntry block = NDS.JIT.LookUpBlock(1, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); else - ARMJIT::CompileBlock(this); + NDS.JIT.CompileBlock(this); if (StopExecution) { @@ -852,17 +877,17 @@ void ARMv4::ExecuteJIT() if (Halted || IdleLoop) { - if ((Halted == 1 || IdleLoop) && NDS::ARM7Timestamp < NDS::ARM7Target) + if ((Halted == 1 || IdleLoop) && NDS.ARM7Timestamp < NDS.ARM7Target) { Cycles = 0; - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; } IdleLoop = 0; break; } } - NDS::ARM7Timestamp += Cycles; + NDS.ARM7Timestamp += Cycles; Cycles = 0; } @@ -871,7 +896,9 @@ void ARMv4::ExecuteJIT() if (Halted == 4) { - DSi::SoftReset(); + assert(NDS.ConsoleType == 1); + auto& dsi = dynamic_cast(NDS); + dsi.SoftReset(); Halted = 2; } } @@ -916,3 +943,402 @@ void ARMv4::FillPipeline() NextInstr[1] = CodeRead32(R[15]); } } + +#ifdef GDBSTUB_ENABLED +u32 ARM::ReadReg(Gdb::Register reg) +{ + using Gdb::Register; + int r = static_cast(reg); + + if (reg < Register::pc) return R[r]; + else if (reg == Register::pc) + { + return R[r] - ((CPSR & 0x20) ? 2 : 4); + } + else if (reg == Register::cpsr) return CPSR; + else if (reg == Register::sp_usr || reg == Register::lr_usr) + { + r -= static_cast(Register::sp_usr); + if (ModeIs(0x10) || ModeIs(0x1f)) + { + return R[13 + r]; + } + else switch (CPSR & 0x1f) + { + case 0x11: return R_FIQ[5 + r]; + case 0x12: return R_IRQ[0 + r]; + case 0x13: return R_SVC[0 + r]; + case 0x17: return R_ABT[0 + r]; + case 0x1b: return R_UND[0 + r]; + } + } + else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq) + { + r -= static_cast(Register::r8_fiq); + return ModeIs(0x11) ? R[ 8 + r] : R_FIQ[r]; + } + else if (reg == Register::sp_irq || reg == Register::lr_irq) + { + r -= static_cast(Register::sp_irq); + return ModeIs(0x12) ? R[13 + r] : R_IRQ[r]; + } + else if (reg == Register::sp_svc || reg == Register::lr_svc) + { + r -= static_cast(Register::sp_svc); + return ModeIs(0x13) ? R[13 + r] : R_SVC[r]; + } + else if (reg == Register::sp_abt || reg == Register::lr_abt) + { + r -= static_cast(Register::sp_abt); + return ModeIs(0x17) ? R[13 + r] : R_ABT[r]; + } + else if (reg == Register::sp_und || reg == Register::lr_und) + { + r -= static_cast(Register::sp_und); + return ModeIs(0x1b) ? R[13 + r] : R_UND[r]; + } + else if (reg == Register::spsr_fiq) return ModeIs(0x11) ? CPSR : R_FIQ[7]; + else if (reg == Register::spsr_irq) return ModeIs(0x12) ? CPSR : R_IRQ[2]; + else if (reg == Register::spsr_svc) return ModeIs(0x13) ? CPSR : R_SVC[2]; + else if (reg == Register::spsr_abt) return ModeIs(0x17) ? CPSR : R_ABT[2]; + else if (reg == Register::spsr_und) return ModeIs(0x1b) ? CPSR : R_UND[2]; + + Log(LogLevel::Warn, "GDB reg read: unknown reg no %d\n", r); + return 0xdeadbeef; +} +void ARM::WriteReg(Gdb::Register reg, u32 v) +{ + using Gdb::Register; + int r = static_cast(reg); + + if (reg < Register::pc) R[r] = v; + else if (reg == Register::pc) JumpTo(v); + else if (reg == Register::cpsr) CPSR = v; + else if (reg == Register::sp_usr || reg == Register::lr_usr) + { + r -= static_cast(Register::sp_usr); + if (ModeIs(0x10) || ModeIs(0x1f)) + { + R[13 + r] = v; + } + else switch (CPSR & 0x1f) + { + case 0x11: R_FIQ[5 + r] = v; break; + case 0x12: R_IRQ[0 + r] = v; break; + case 0x13: R_SVC[0 + r] = v; break; + case 0x17: R_ABT[0 + r] = v; break; + case 0x1b: R_UND[0 + r] = v; break; + } + } + else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq) + { + r -= static_cast(Register::r8_fiq); + *(ModeIs(0x11) ? &R[ 8 + r] : &R_FIQ[r]) = v; + } + else if (reg == Register::sp_irq || reg == Register::lr_irq) + { + r -= static_cast(Register::sp_irq); + *(ModeIs(0x12) ? &R[13 + r] : &R_IRQ[r]) = v; + } + else if (reg == Register::sp_svc || reg == Register::lr_svc) + { + r -= static_cast(Register::sp_svc); + *(ModeIs(0x13) ? &R[13 + r] : &R_SVC[r]) = v; + } + else if (reg == Register::sp_abt || reg == Register::lr_abt) + { + r -= static_cast(Register::sp_abt); + *(ModeIs(0x17) ? &R[13 + r] : &R_ABT[r]) = v; + } + else if (reg == Register::sp_und || reg == Register::lr_und) + { + r -= static_cast(Register::sp_und); + *(ModeIs(0x1b) ? &R[13 + r] : &R_UND[r]) = v; + } + else if (reg == Register::spsr_fiq) + { + *(ModeIs(0x11) ? &CPSR : &R_FIQ[7]) = v; + } + else if (reg == Register::spsr_irq) + { + *(ModeIs(0x12) ? &CPSR : &R_IRQ[2]) = v; + } + else if (reg == Register::spsr_svc) + { + *(ModeIs(0x13) ? &CPSR : &R_SVC[2]) = v; + } + else if (reg == Register::spsr_abt) + { + *(ModeIs(0x17) ? &CPSR : &R_ABT[2]) = v; + } + else if (reg == Register::spsr_und) + { + *(ModeIs(0x1b) ? &CPSR : &R_UND[2]) = v; + } + else Log(LogLevel::Warn, "GDB reg write: unknown reg no %d (write 0x%08x)\n", r, v); +} +u32 ARM::ReadMem(u32 addr, int size) +{ + if (size == 8) return BusRead8(addr); + else if (size == 16) return BusRead16(addr); + else if (size == 32) return BusRead32(addr); + else return 0xfeedface; +} +void ARM::WriteMem(u32 addr, int size, u32 v) +{ + if (size == 8) BusWrite8(addr, (u8)v); + else if (size == 16) BusWrite16(addr, (u16)v); + else if (size == 32) BusWrite32(addr, v); +} + +void ARM::ResetGdb() +{ + NDS.Reset(); + NDS.GPU.StartFrame(); // need this to properly kick off the scheduler & frame output +} +int ARM::RemoteCmd(const u8* cmd, size_t len) +{ + (void)len; + + Log(LogLevel::Info, "[ARMGDB] Rcmd: \"%s\"\n", cmd); + if (!strcmp((const char*)cmd, "reset") || !strcmp((const char*)cmd, "r")) + { + Reset(); + return 0; + } + + return 1; // not implemented (yet) +} + +void ARMv5::WriteMem(u32 addr, int size, u32 v) +{ + if (addr < ITCMSize) + { + if (size == 8) *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u8)v; + else if (size == 16) *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u16)v; + else if (size == 32) *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u32)v; + else {} + return; + } + else if ((addr & DTCMMask) == DTCMBase) + { + if (size == 8) *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u8)v; + else if (size == 16) *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u16)v; + else if (size == 32) *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u32)v; + else {} + return; + } + + ARM::WriteMem(addr, size, v); +} +u32 ARMv5::ReadMem(u32 addr, int size) +{ + if (addr < ITCMSize) + { + if (size == 8) return *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + else if (size == 16) return *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + else if (size == 32) return *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + else return 0xfeedface; + } + else if ((addr & DTCMMask) == DTCMBase) + { + if (size == 8) return *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + else if (size == 16) return *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + else if (size == 32) return *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + else return 0xfeedface; + } + + return ARM::ReadMem(addr, size); +} +#endif + +void ARMv4::DataRead8(u32 addr, u32* val) +{ + *val = BusRead8(addr); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataRead16(u32 addr, u32* val) +{ + addr &= ~1; + + *val = BusRead16(addr); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataRead32(u32 addr, u32* val) +{ + addr &= ~3; + + *val = BusRead32(addr); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][2]; +} + +void ARMv4::DataRead32S(u32 addr, u32* val) +{ + addr &= ~3; + + *val = BusRead32(addr); + DataCycles += NDS.ARM7MemTimings[addr >> 15][3]; +} + +void ARMv4::DataWrite8(u32 addr, u8 val) +{ + BusWrite8(addr, val); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataWrite16(u32 addr, u16 val) +{ + addr &= ~1; + + BusWrite16(addr, val); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataWrite32(u32 addr, u32 val) +{ + addr &= ~3; + + BusWrite32(addr, val); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][2]; +} + +void ARMv4::DataWrite32S(u32 addr, u32 val) +{ + addr &= ~3; + + BusWrite32(addr, val); + DataCycles += NDS.ARM7MemTimings[addr >> 15][3]; +} + + +void ARMv4::AddCycles_C() +{ + // code only. this code fetch is sequential. + Cycles += NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3]; +} + +void ARMv4::AddCycles_CI(s32 num) +{ + // code+internal. results in a nonseq code fetch. + Cycles += NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num; +} + +void ARMv4::AddCycles_CDI() +{ + // LDR/LDM cycles. + s32 numC = NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; + s32 numD = DataCycles; + + if ((DataRegion >> 24) == 0x02) // mainRAM + { + if (CodeRegion == 0x02) + Cycles += numC + numD; + else + { + numC++; + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + } + else if (CodeRegion == 0x02) + { + numD++; + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + else + { + Cycles += numC + numD + 1; + } +} + +void ARMv4::AddCycles_CD() +{ + // TODO: max gain should be 5c when writing to mainRAM + s32 numC = NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; + s32 numD = DataCycles; + + if ((DataRegion >> 24) == 0x02) + { + if (CodeRegion == 0x02) + Cycles += numC + numD; + else + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + else if (CodeRegion == 0x02) + { + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + else + { + Cycles += numC + numD; + } +} + +u8 ARMv5::BusRead8(u32 addr) +{ + return NDS.ARM9Read8(addr); +} + +u16 ARMv5::BusRead16(u32 addr) +{ + return NDS.ARM9Read16(addr); +} + +u32 ARMv5::BusRead32(u32 addr) +{ + return NDS.ARM9Read32(addr); +} + +void ARMv5::BusWrite8(u32 addr, u8 val) +{ + NDS.ARM9Write8(addr, val); +} + +void ARMv5::BusWrite16(u32 addr, u16 val) +{ + NDS.ARM9Write16(addr, val); +} + +void ARMv5::BusWrite32(u32 addr, u32 val) +{ + NDS.ARM9Write32(addr, val); +} + +u8 ARMv4::BusRead8(u32 addr) +{ + return NDS.ARM7Read8(addr); +} + +u16 ARMv4::BusRead16(u32 addr) +{ + return NDS.ARM7Read16(addr); +} + +u32 ARMv4::BusRead32(u32 addr) +{ + return NDS.ARM7Read32(addr); +} + +void ARMv4::BusWrite8(u32 addr, u8 val) +{ + NDS.ARM7Write8(addr, val); +} + +void ARMv4::BusWrite16(u32 addr, u16 val) +{ + NDS.ARM7Write16(addr, val); +} + +void ARMv4::BusWrite32(u32 addr, u32 val) +{ + NDS.ARM7Write32(addr, val); +} +} + diff --git a/src/ARM.h b/src/ARM.h index 6cfb6772..1e0b71b8 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,10 +20,18 @@ #define ARM_H #include +#include #include "types.h" -#include "NDS.h" +#include "MemRegion.h" +#include "MemConstants.h" +#ifdef GDBSTUB_ENABLED +#include "debug/GdbStub.h" +#endif + +namespace melonDS +{ inline u32 ROR(u32 x, u32 n) { return (x >> (n&0x1F)) | (x << ((32-n)&0x1F)); @@ -35,13 +43,20 @@ enum RWFlags_ForceUser = (1<<21), }; -const u32 ITCMPhysicalSize = 0x8000; -const u32 DTCMPhysicalSize = 0x4000; +struct GDBArgs; +class ARMJIT; +class GPU; +class ARMJIT_Memory; +class NDS; +class Savestate; class ARM +#ifdef GDBSTUB_ENABLED + : public Gdb::StubCallbacks +#endif { public: - ARM(u32 num); + ARM(u32 num, bool jit, std::optional gdb, NDS& nds); virtual ~ARM(); // destroy shit virtual void Reset(); @@ -59,12 +74,13 @@ public: Halted = halt; } + void NocashPrint(u32 addr) noexcept; virtual void Execute() = 0; #ifdef JIT_ENABLED virtual void ExecuteJIT() = 0; #endif - bool CheckCondition(u32 code) + bool CheckCondition(u32 code) const { if (code == 0xE) return true; if (ConditionTable[code] & (1 << (CPSR>>28))) return true; @@ -93,6 +109,18 @@ public: if (v) CPSR |= 0x10000000; } + inline bool ModeIs(u32 mode) const + { + u32 cm = CPSR & 0x1f; + mode &= 0x1f; + + if (mode == cm) return true; + if (mode == 0x17) return cm >= 0x14 && cm <= 0x17; // abt + if (mode == 0x1b) return cm >= 0x18 && cm <= 0x1b; // und + + return false; + } + void UpdateMode(u32 oldmode, u32 newmode, bool phony = false); void TriggerIRQ(); @@ -114,6 +142,7 @@ public: virtual void AddCycles_CDI() = 0; virtual void AddCycles_CD() = 0; + void CheckGdbIncoming(); u32 Num; @@ -147,75 +176,103 @@ public: u32 ExceptionBase; - NDS::MemRegion CodeMem; + MemRegion CodeMem; #ifdef JIT_ENABLED u32 FastBlockLookupStart, FastBlockLookupSize; u64* FastBlockLookup; #endif - static u32 ConditionTable[16]; + static const u32 ConditionTable[16]; +#ifdef GDBSTUB_ENABLED + Gdb::GdbStub GdbStub; +#endif + + melonDS::NDS& NDS; +protected: + virtual u8 BusRead8(u32 addr) = 0; + virtual u16 BusRead16(u32 addr) = 0; + virtual u32 BusRead32(u32 addr) = 0; + virtual void BusWrite8(u32 addr, u8 val) = 0; + virtual void BusWrite16(u32 addr, u16 val) = 0; + virtual void BusWrite32(u32 addr, u32 val) = 0; + +#ifdef GDBSTUB_ENABLED + bool IsSingleStep; + bool BreakReq; + bool BreakOnStartup; + u16 Port; + +public: + int GetCPU() const override { return Num ? 7 : 9; } + + u32 ReadReg(Gdb::Register reg) override; + void WriteReg(Gdb::Register reg, u32 v) override; + u32 ReadMem(u32 addr, int size) override; + void WriteMem(u32 addr, int size, u32 v) override; + + void ResetGdb() override; + int RemoteCmd(const u8* cmd, size_t len) override; protected: - u8 (*BusRead8)(u32 addr); - u16 (*BusRead16)(u32 addr); - u32 (*BusRead32)(u32 addr); - void (*BusWrite8)(u32 addr, u8 val); - void (*BusWrite16)(u32 addr, u16 val); - void (*BusWrite32)(u32 addr, u32 val); +#endif + + void GdbCheckA(); + void GdbCheckB(); + void GdbCheckC(); }; class ARMv5 : public ARM { public: - ARMv5(); + ARMv5(melonDS::NDS& nds, std::optional gdb, bool jit); ~ARMv5(); - void Reset(); + void Reset() override; - void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; void UpdateRegionTimings(u32 addrstart, u32 addrend); - void FillPipeline(); + void FillPipeline() override; - void JumpTo(u32 addr, bool restorecpsr = false); + void JumpTo(u32 addr, bool restorecpsr = false) override; void PrefetchAbort(); void DataAbort(); - void Execute(); + void Execute() override; #ifdef JIT_ENABLED - void ExecuteJIT(); + void ExecuteJIT() override; #endif // all code accesses are forced nonseq 32bit u32 CodeRead32(u32 addr, bool branch); - void DataRead8(u32 addr, u32* val); - void DataRead16(u32 addr, u32* val); - void DataRead32(u32 addr, u32* val); - void DataRead32S(u32 addr, u32* val); - void DataWrite8(u32 addr, u8 val); - void DataWrite16(u32 addr, u16 val); - void DataWrite32(u32 addr, u32 val); - void DataWrite32S(u32 addr, u32 val); + void DataRead8(u32 addr, u32* val) override; + void DataRead16(u32 addr, u32* val) override; + void DataRead32(u32 addr, u32* val) override; + void DataRead32S(u32 addr, u32* val) override; + void DataWrite8(u32 addr, u8 val) override; + void DataWrite16(u32 addr, u16 val) override; + void DataWrite32(u32 addr, u32 val) override; + void DataWrite32S(u32 addr, u32 val) override; - void AddCycles_C() + void AddCycles_C() override { // code only. always nonseq 32-bit for ARM9. s32 numC = (R[15] & 0x2) ? 0 : CodeCycles; Cycles += numC; } - void AddCycles_CI(s32 numI) + void AddCycles_CI(s32 numI) override { // code+internal s32 numC = (R[15] & 0x2) ? 0 : CodeCycles; Cycles += numC + numI; } - void AddCycles_CDI() + void AddCycles_CDI() override { // LDR/LDM cycles. ARM9 seems to skip the internal cycle there. // TODO: ITCM data fetches shouldn't be parallelized, they say @@ -228,7 +285,7 @@ public: // Cycles += numC + numD; } - void AddCycles_CD() + void AddCycles_CD() override { // TODO: ITCM data fetches shouldn't be parallelized, they say s32 numC = (R[15] & 0x2) ? 0 : CodeCycles; @@ -240,7 +297,7 @@ public: // Cycles += numC + numD; } - void GetCodeMemRegion(u32 addr, NDS::MemRegion* region); + void GetCodeMemRegion(u32 addr, MemRegion* region); void CP15Reset(); void CP15DoSavestate(Savestate* file); @@ -258,7 +315,7 @@ public: void ICacheInvalidateAll(); void CP15Write(u32 id, u32 val); - u32 CP15Read(u32 id); + u32 CP15Read(u32 id) const; u32 CP15Control; @@ -301,23 +358,34 @@ public: u8* CurICacheLine; - bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region); + bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region); + +#ifdef GDBSTUB_ENABLED + u32 ReadMem(u32 addr, int size) override; + void WriteMem(u32 addr, int size, u32 v) override; +#endif + +protected: + u8 BusRead8(u32 addr) override; + u16 BusRead16(u32 addr) override; + u32 BusRead32(u32 addr) override; + void BusWrite8(u32 addr, u8 val) override; + void BusWrite16(u32 addr, u16 val) override; + void BusWrite32(u32 addr, u32 val) override; }; class ARMv4 : public ARM { public: - ARMv4(); + ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit); - void Reset(); + void FillPipeline() override; - void FillPipeline(); + void JumpTo(u32 addr, bool restorecpsr = false) override; - void JumpTo(u32 addr, bool restorecpsr = false); - - void Execute(); + void Execute() override; #ifdef JIT_ENABLED - void ExecuteJIT(); + void ExecuteJIT() override; #endif u16 CodeRead16(u32 addr) @@ -330,134 +398,25 @@ public: return BusRead32(addr); } - void DataRead8(u32 addr, u32* val) - { - *val = BusRead8(addr); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataRead16(u32 addr, u32* val) - { - addr &= ~1; - - *val = BusRead16(addr); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataRead32(u32 addr, u32* val) - { - addr &= ~3; - - *val = BusRead32(addr); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][2]; - } - - void DataRead32S(u32 addr, u32* val) - { - addr &= ~3; - - *val = BusRead32(addr); - DataCycles += NDS::ARM7MemTimings[addr >> 15][3]; - } - - void DataWrite8(u32 addr, u8 val) - { - BusWrite8(addr, val); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataWrite16(u32 addr, u16 val) - { - addr &= ~1; - - BusWrite16(addr, val); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataWrite32(u32 addr, u32 val) - { - addr &= ~3; - - BusWrite32(addr, val); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][2]; - } - - void DataWrite32S(u32 addr, u32 val) - { - addr &= ~3; - - BusWrite32(addr, val); - DataCycles += NDS::ARM7MemTimings[addr >> 15][3]; - } - - - void AddCycles_C() - { - // code only. this code fetch is sequential. - Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3]; - } - - void AddCycles_CI(s32 num) - { - // code+internal. results in a nonseq code fetch. - Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num; - } - - void AddCycles_CDI() - { - // LDR/LDM cycles. - s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; - s32 numD = DataCycles; - - if ((DataRegion >> 24) == 0x02) // mainRAM - { - if (CodeRegion == 0x02) - Cycles += numC + numD; - else - { - numC++; - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - } - else if (CodeRegion == 0x02) - { - numD++; - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - else - { - Cycles += numC + numD + 1; - } - } - - void AddCycles_CD() - { - // TODO: max gain should be 5c when writing to mainRAM - s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; - s32 numD = DataCycles; - - if ((DataRegion >> 24) == 0x02) - { - if (CodeRegion == 0x02) - Cycles += numC + numD; - else - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - else if (CodeRegion == 0x02) - { - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - else - { - Cycles += numC + numD; - } - } + void DataRead8(u32 addr, u32* val) override; + void DataRead16(u32 addr, u32* val) override; + void DataRead32(u32 addr, u32* val) override; + void DataRead32S(u32 addr, u32* val) override; + void DataWrite8(u32 addr, u8 val) override; + void DataWrite16(u32 addr, u16 val) override; + void DataWrite32(u32 addr, u32 val) override; + void DataWrite32S(u32 addr, u32 val) override; + void AddCycles_C() override; + void AddCycles_CI(s32 num) override; + void AddCycles_CDI() override; + void AddCycles_CD() override; +protected: + u8 BusRead8(u32 addr) override; + u16 BusRead16(u32 addr) override; + u32 BusRead32(u32 addr) override; + void BusWrite8(u32 addr, u8 val) override; + void BusWrite16(u32 addr, u16 val) override; + void BusWrite32(u32 addr, u32 val) override; }; namespace ARMInterpreter @@ -467,13 +426,5 @@ void A_UNK(ARM* cpu); void T_UNK(ARM* cpu); } - -namespace NDS -{ - -extern ARMv5* ARM9; -extern ARMv4* ARM7; - } - #endif // ARM_H diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 35854d16..ff73e230 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,16 +24,22 @@ #include "ARMInterpreter_LoadStore.h" #include "Platform.h" -using Platform::Log; -using Platform::LogLevel; +#ifdef GDBSTUB_ENABLED +#include "debug/GdbStub.h" +#endif -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { + using Platform::Log; + using Platform::LogLevel; void A_UNK(ARM* cpu) { Log(LogLevel::Warn, "undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8); +#ifdef GDBSTUB_ENABLED + cpu->GdbStub.Enter(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-8); +#endif //for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]); //NDS::Halt(); u32 oldcpsr = cpu->CPSR; @@ -49,6 +55,9 @@ void A_UNK(ARM* cpu) void T_UNK(ARM* cpu) { Log(LogLevel::Warn, "undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4); +#ifdef GDBSTUB_ENABLED + cpu->GdbStub.Enter(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-4); +#endif //NDS::Halt(); u32 oldcpsr = cpu->CPSR; cpu->CPSR &= ~0xBF; diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h index 9a0fc28e..cff4821a 100644 --- a/src/ARMInterpreter.h +++ b/src/ARMInterpreter.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,6 +22,8 @@ #include "types.h" #include "ARM.h" +namespace melonDS +{ namespace ARMInterpreter { @@ -41,4 +43,5 @@ void A_BLX_IMM(ARM* cpu); // I'm a special one look at me } +} #endif // ARMINTERPRETER_H diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 95b6f379..315d59d0 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -18,8 +18,9 @@ #include #include "ARM.h" +#include "NDS.h" -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { inline bool CarryAdd(u32 a, u32 b) @@ -692,7 +693,7 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu) // but since they serve no purpose ATTOW, we can skip them u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags // TODO: Pass flags to NocashPrint - NDS::NocashPrint(cpu->Num, addr); + cpu->NDS.NocashPrint(cpu->Num, addr); } } @@ -1561,7 +1562,7 @@ void T_MOV_HIREG(ARM* cpu) // but since they serve no purpose ATTOW, we can skip them u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags // TODO: Pass flags to NocashPrint - NDS::NocashPrint(cpu->Num, addr); + cpu->NDS.NocashPrint(cpu->Num, addr); } } diff --git a/src/ARMInterpreter_ALU.h b/src/ARMInterpreter_ALU.h index 8dcc3118..6998b637 100644 --- a/src/ARMInterpreter_ALU.h +++ b/src/ARMInterpreter_ALU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,8 @@ #ifndef ARMINTERPRETER_ALU_H #define ARMINTERPRETER_ALU_H +namespace melonDS +{ namespace ARMInterpreter { @@ -134,4 +136,5 @@ void T_ADD_SP(ARM* cpu); } +} #endif diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp index ab66395e..015f5682 100644 --- a/src/ARMInterpreter_Branch.cpp +++ b/src/ARMInterpreter_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,12 +19,11 @@ #include "ARM.h" #include "Platform.h" +namespace melonDS::ARMInterpreter +{ using Platform::Log; using Platform::LogLevel; -namespace ARMInterpreter -{ - void A_B(ARM* cpu) { diff --git a/src/ARMInterpreter_Branch.h b/src/ARMInterpreter_Branch.h index 775e8957..51a561c1 100644 --- a/src/ARMInterpreter_Branch.h +++ b/src/ARMInterpreter_Branch.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,8 @@ #ifndef ARMINTERPRETER_BRANCH_H #define ARMINTERPRETER_BRANCH_H +namespace melonDS +{ namespace ARMInterpreter { @@ -36,4 +38,5 @@ void T_BL_LONG_2(ARM* cpu); } +} #endif diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index e7b83eb9..91acaacc 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,7 +20,7 @@ #include "ARM.h" -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { @@ -62,14 +62,20 @@ namespace ARMInterpreter #define A_STR \ offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \ - cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \ + u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \ + if (((cpu->CurInstr>>12) & 0xF) == 0xF) \ + storeval += 4; \ + cpu->DataWrite32(offset, storeval); \ if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \ cpu->AddCycles_CD(); // TODO: user mode (bit21) #define A_STR_POST \ u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \ - cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \ + u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \ + if (((cpu->CurInstr>>12) & 0xF) == 0xF) \ + storeval += 4; \ + cpu->DataWrite32(addr, storeval); \ cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \ cpu->AddCycles_CD(); diff --git a/src/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h index efd4f180..32d6e4d2 100644 --- a/src/ARMInterpreter_LoadStore.h +++ b/src/ARMInterpreter_LoadStore.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,7 +19,7 @@ #ifndef ARMINTERPRETER_LOADSTORE_H #define ARMINTERPRETER_LOADSTORE_H -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { #define A_PROTO_WB_LDRSTR(x) \ diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 77bb50dc..c3fcba26 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -17,7 +17,7 @@ */ #include "ARMJIT.h" - +#include "ARMJIT_Memory.h" #include #include #include @@ -43,138 +43,51 @@ #include "Wifi.h" #include "NDSCart.h" #include "Platform.h" +#include "ARMJIT_x64/ARMJIT_Offsets.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -#include "ARMJIT_x64/ARMJIT_Offsets.h" static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, ""); static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset, ""); static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset, ""); -namespace ARMJIT -{ #define JIT_DEBUGPRINT(msg, ...) //#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, msg, ## __VA_ARGS__) -Compiler* JITCompiler; - -int MaxBlockSize; -bool LiteralOptimizations; -bool BranchOptimizations; -bool FastMemory; - - -std::unordered_map JitBlocks9; -std::unordered_map JitBlocks7; - -std::unordered_map RestoreCandidates; - -TinyVector InvalidLiterals; - -AddressRange CodeIndexITCM[ITCMPhysicalSize / 512]; -AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512]; -AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512]; -AddressRange CodeIndexVRAM[0x100000 / 512]; -AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512]; -AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512]; -AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512]; -AddressRange CodeIndexARM7WVRAM[0x40000 / 512]; -AddressRange CodeIndexBIOS9DSi[0x10000 / 512]; -AddressRange CodeIndexBIOS7DSi[0x10000 / 512]; -AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512]; -AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512]; -AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512]; - -u64 FastBlockLookupITCM[ITCMPhysicalSize / 2]; -u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2]; -u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2]; -u64 FastBlockLookupVRAM[0x100000 / 2]; -u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2]; -u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2]; -u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2]; -u64 FastBlockLookupARM7WVRAM[0x40000 / 2]; -u64 FastBlockLookupBIOS9DSi[0x10000 / 2]; -u64 FastBlockLookupBIOS7DSi[0x10000 / 2]; -u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2]; -u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2]; -u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2]; - const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] = { 0, ITCMPhysicalSize, 0, - sizeof(NDS::ARM9BIOS), - NDS::MainRAMMaxSize, - NDS::SharedWRAMSize, + ARM9BIOSSize, + MainRAMMaxSize, + SharedWRAMSize, 0, 0x100000, - sizeof(NDS::ARM7BIOS), - NDS::ARM7WRAMSize, + ARM7BIOSSize, + ARM7WRAMSize, 0, 0, 0x40000, 0x10000, 0x10000, - DSi::NWRAMSize, - DSi::NWRAMSize, - DSi::NWRAMSize, + NWRAMSize, + NWRAMSize, + NWRAMSize, }; -AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] = -{ - NULL, - CodeIndexITCM, - NULL, - CodeIndexARM9BIOS, - CodeIndexMainRAM, - CodeIndexSWRAM, - NULL, - CodeIndexVRAM, - CodeIndexARM7BIOS, - CodeIndexARM7WRAM, - NULL, - NULL, - CodeIndexARM7WVRAM, - CodeIndexBIOS9DSi, - CodeIndexBIOS7DSi, - CodeIndexNWRAM_A, - CodeIndexNWRAM_B, - CodeIndexNWRAM_C -}; - -u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] = -{ - NULL, - FastBlockLookupITCM, - NULL, - FastBlockLookupARM9BIOS, - FastBlockLookupMainRAM, - FastBlockLookupSWRAM, - NULL, - FastBlockLookupVRAM, - FastBlockLookupARM7BIOS, - FastBlockLookupARM7WRAM, - NULL, - NULL, - FastBlockLookupARM7WVRAM, - FastBlockLookupBIOS9DSi, - FastBlockLookupBIOS7DSi, - FastBlockLookupNWRAM_A, - FastBlockLookupNWRAM_B, - FastBlockLookupNWRAM_C -}; - -u32 LocaliseCodeAddress(u32 num, u32 addr) +u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept { int region = num == 0 - ? ARMJIT_Memory::ClassifyAddress9(addr) - : ARMJIT_Memory::ClassifyAddress7(addr); + ? Memory.ClassifyAddress9(addr) + : Memory.ClassifyAddress7(addr); if (CodeMemRegions[region]) - return ARMJIT_Memory::LocaliseAddress(region, num, addr); + return Memory.LocaliseAddress(region, num, addr); return 0; } @@ -190,11 +103,31 @@ T SlowRead9(u32 addr, ARMv5* cpu) else if ((addr & cpu->DTCMMask) == cpu->DTCMBase) val = *(T*)&cpu->DTCM[addr & 0x3FFF]; else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr); + val = NDS::Current->ARM9Read32(addr); else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM9Read16 : DSi::ARM9Read16)(addr); + val = NDS::Current->ARM9Read16(addr); else - val = (ConsoleType == 0 ? NDS::ARM9Read8 : DSi::ARM9Read8)(addr); + val = NDS::Current->ARM9Read8(addr); + + if (std::is_same::value) + return ROR(val, offset << 3); + else + return val; +} + +template +T SlowRead7(u32 addr) +{ + u32 offset = addr & 0x3; + addr &= ~(sizeof(T) - 1); + + T val; + if (std::is_same::value) + val = NDS::Current->ARM7Read32(addr); + else if (std::is_same::value) + val = NDS::Current->ARM7Read16(addr); + else + val = NDS::Current->ARM7Read8(addr); if (std::is_same::value) return ROR(val, offset << 3); @@ -209,7 +142,7 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) if (addr < cpu->ITCMSize) { - CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + cpu->NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); *(T*)&cpu->ITCM[addr & 0x7FFF] = val; } else if ((addr & cpu->DTCMMask) == cpu->DTCMBase) @@ -218,49 +151,29 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) } else if (std::is_same::value) { - (ConsoleType == 0 ? NDS::ARM9Write32 : DSi::ARM9Write32)(addr, val); + NDS::Current->ARM9Write32(addr, val); } else if (std::is_same::value) { - (ConsoleType == 0 ? NDS::ARM9Write16 : DSi::ARM9Write16)(addr, val); + NDS::Current->ARM9Write16(addr, val); } else { - (ConsoleType == 0 ? NDS::ARM9Write8 : DSi::ARM9Write8)(addr, val); + NDS::Current->ARM9Write8(addr, val); } } -template -T SlowRead7(u32 addr) -{ - u32 offset = addr & 0x3; - addr &= ~(sizeof(T) - 1); - - T val; - if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr); - else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr); - else - val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr); - - if (std::is_same::value) - return ROR(val, offset << 3); - else - return val; -} - template void SlowWrite7(u32 addr, u32 val) { addr &= ~(sizeof(T) - 1); if (std::is_same::value) - (ConsoleType == 0 ? NDS::ARM7Write32 : DSi::ARM7Write32)(addr, val); + NDS::Current->ARM7Write32(addr, val); else if (std::is_same::value) - (ConsoleType == 0 ? NDS::ARM7Write16 : DSi::ARM7Write16)(addr, val); + NDS::Current->ARM7Write16(addr, val); else - (ConsoleType == 0 ? NDS::ARM7Write8 : DSi::ARM7Write8)(addr, val); + NDS::Current->ARM7Write8(addr, val); } template @@ -316,39 +229,18 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num) INSTANTIATE_SLOWMEM(0) INSTANTIATE_SLOWMEM(1) -void Init() -{ - JITCompiler = new Compiler(); - - ARMJIT_Memory::Init(); -} - -void DeInit() +ARMJIT::~ARMJIT() noexcept { JitEnableWrite(); ResetBlockCache(); - ARMJIT_Memory::DeInit(); - - delete JITCompiler; - JITCompiler = nullptr; } -void Reset() +void ARMJIT::Reset() noexcept { - MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize); - LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations); - BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations); - FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory); - - if (MaxBlockSize < 1) - MaxBlockSize = 1; - if (MaxBlockSize > 32) - MaxBlockSize = 32; - JitEnableWrite(); ResetBlockCache(); - ARMJIT_Memory::Reset(); + Memory.Reset(); } void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags) @@ -575,7 +467,7 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] = }; #undef F -void RetireJitBlock(JitBlock* block) +void ARMJIT::RetireJitBlock(JitBlock* block) noexcept { auto it = RestoreCandidates.find(block->InstrHash); if (it != RestoreCandidates.end()) @@ -589,7 +481,57 @@ void RetireJitBlock(JitBlock* block) } } -void CompileBlock(ARM* cpu) +void ARMJIT::SetJITArgs(JITArgs args) noexcept +{ + args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u); + + if (MaxBlockSize != args.MaxBlockSize + || LiteralOptimizations != args.LiteralOptimizations + || BranchOptimizations != args.BranchOptimizations + || FastMemory != args.FastMemory) + ResetBlockCache(); + + MaxBlockSize = args.MaxBlockSize; + LiteralOptimizations = args.LiteralOptimizations; + BranchOptimizations = args.BranchOptimizations; + FastMemory = args.FastMemory; +} + +void ARMJIT::SetMaxBlockSize(int size) noexcept +{ + size = std::clamp(size, 1, 32); + + if (size != MaxBlockSize) + ResetBlockCache(); + + MaxBlockSize = size; +} + +void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept +{ + if (LiteralOptimizations != enabled) + ResetBlockCache(); + + LiteralOptimizations = enabled; +} + +void ARMJIT::SetBranchOptimizations(bool enabled) noexcept +{ + if (BranchOptimizations != enabled) + ResetBlockCache(); + + BranchOptimizations = enabled; +} + +void ARMJIT::SetFastMemory(bool enabled) noexcept +{ + if (FastMemory != enabled) + ResetBlockCache(); + + FastMemory = enabled; +} + +void ARMJIT::CompileBlock(ARM* cpu) noexcept { bool thumb = cpu->CPSR & 0x20; @@ -616,7 +558,7 @@ void CompileBlock(ARM* cpu) u64* entry = &FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]; *entry = ((u64)blockAddr | cpu->Num) << 32; - *entry |= JITCompiler->SubEntryOffset(existingBlockIt->second->EntryPoint); + *entry |= JITCompiler.SubEntryOffset(existingBlockIt->second->EntryPoint); return; } @@ -717,7 +659,7 @@ void CompileBlock(ARM* cpu) nextInstr[1] = cpuv4->CodeRead32(r15); instrs[i].CodeCycles = cpu->CodeCycles; } - instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr); + instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr, LiteralOptimizations); hasMemoryInstr |= thumb ? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA) @@ -875,7 +817,7 @@ void CompileBlock(ARM* cpu) i++; - bool canCompile = JITCompiler->CanCompile(thumb, instrs[i - 1].Info.Kind); + bool canCompile = JITCompiler.CanCompile(thumb, instrs[i - 1].Info.Kind); bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken)); if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond) FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF); @@ -956,7 +898,7 @@ void CompileBlock(ARM* cpu) FloodFillSetFlags(instrs, i - 1, 0xF); JitEnableWrite(); - block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr); + block->EntryPoint = JITCompiler.CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr); JitEnableExecute(); JIT_DEBUGPRINT("block start %p\n", block->EntryPoint); @@ -977,7 +919,7 @@ void CompileBlock(ARM* cpu) AddressRange* region = CodeMemRegions[addressRanges[j] >> 27]; if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512])) - ARMJIT_Memory::SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true); + Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true); AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512]; range->Code |= addressMasks[j]; @@ -991,10 +933,10 @@ void CompileBlock(ARM* cpu) u64* entry = &FastBlockLookupRegions[(localAddr >> 27)][(localAddr & 0x7FFFFFF) / 2]; *entry = ((u64)blockAddr | cpu->Num) << 32; - *entry |= JITCompiler->SubEntryOffset(block->EntryPoint); + *entry |= JITCompiler.SubEntryOffset(block->EntryPoint); } -void InvalidateByAddr(u32 localAddr) +void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept { JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr); @@ -1031,7 +973,7 @@ void InvalidateByAddr(u32 localAddr) if (range->Blocks.Length == 0 && !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512])) { - ARMJIT_Memory::SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false); + Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false); } bool literalInvalidation = false; @@ -1064,7 +1006,7 @@ void InvalidateByAddr(u32 localAddr) if (otherRange->Blocks.Length == 0) { if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512])) - ARMJIT_Memory::SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false); + Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false); otherRange->Code = 0; } @@ -1088,7 +1030,7 @@ void InvalidateByAddr(u32 localAddr) } } -void CheckAndInvalidateITCM() +void ARMJIT::CheckAndInvalidateITCM() noexcept { for (u32 i = 0; i < ITCMPhysicalSize; i+=512) { @@ -1106,7 +1048,7 @@ void CheckAndInvalidateITCM() } } -void CheckAndInvalidateWVRAM(int bank) +void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept { u32 start = bank == 1 ? 0x20000 : 0; for (u32 i = start; i < start+0x20000; i+=512) @@ -1122,38 +1064,30 @@ void CheckAndInvalidateWVRAM(int bank) } } -template -void CheckAndInvalidate(u32 addr) -{ - u32 localAddr = ARMJIT_Memory::LocaliseAddress(region, num, addr); - if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16))) - InvalidateByAddr(localAddr); -} - -JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) +JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept { u64* entry = &entries[offset / 2]; if (*entry >> 32 == (addr | num)) - return JITCompiler->AddEntryOffset((u32)*entry); + return JITCompiler.AddEntryOffset((u32)*entry); return NULL; } -void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) +void ARMJIT::blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept { u32 localAddr = LocaliseCodeAddress(num, blockAddr); - assert(JITCompiler->AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry); + assert(JITCompiler.AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry); } -bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) +bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept { // amazingly ignoring the DTCM is the proper behaviour for code fetches int region = num == 0 - ? ARMJIT_Memory::ClassifyAddress9(blockAddr) - : ARMJIT_Memory::ClassifyAddress7(blockAddr); + ? Memory.ClassifyAddress9(blockAddr) + : Memory.ClassifyAddress7(blockAddr); u32 memoryOffset; if (FastBlockLookupRegions[region] - && ARMJIT_Memory::GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size)) + && Memory.GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size)) { //printf("setup exec region %d %d %08x %08x %x %x\n", num, region, blockAddr, start, size, memoryOffset); entry = FastBlockLookupRegions[region] + memoryOffset / 2; @@ -1162,28 +1096,28 @@ bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& return false; } -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept; -void ResetBlockCache() +void ARMJIT::ResetBlockCache() noexcept { Log(LogLevel::Debug, "Resetting JIT block cache...\n"); // could be replace through a function which only resets // the permissions but we're too lazy - ARMJIT_Memory::Reset(); + Memory.Reset(); InvalidLiterals.Clear(); for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++) @@ -1221,10 +1155,10 @@ void ResetBlockCache() JitBlocks9.clear(); JitBlocks7.clear(); - JITCompiler->Reset(); + JITCompiler.Reset(); } -void JitEnableWrite() +void ARMJIT::JitEnableWrite() noexcept { #if defined(__APPLE__) && defined(__aarch64__) if (__builtin_available(macOS 11.0, *)) @@ -1232,7 +1166,7 @@ void JitEnableWrite() #endif } -void JitEnableExecute() +void ARMJIT::JitEnableExecute() noexcept { #if defined(__APPLE__) && defined(__aarch64__) if (__builtin_available(macOS 11.0, *)) diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 97c79cd9..7619f234 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,49 +19,195 @@ #ifndef ARMJIT_H #define ARMJIT_H +#include +#include +#include #include "types.h" +#include "MemConstants.h" +#include "Args.h" +#include "ARMJIT_Memory.h" -#include "ARM.h" -#include "ARM_InstrInfo.h" +#ifdef JIT_ENABLED +#include "JitBlock.h" #if defined(__APPLE__) && defined(__aarch64__) #include #endif -namespace ARMJIT +#include "ARMJIT_Compiler.h" + +namespace melonDS { +class ARM; -typedef void (*JitBlockEntry)(); +class JitBlock; +class ARMJIT +{ +public: + ARMJIT(melonDS::NDS& nds, std::optional jit) noexcept : + NDS(nds), + Memory(nds), + JITCompiler(nds), + MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32), + LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false), + BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false), + FastMemory(jit.has_value() ? jit->FastMemory : false) + {} + ~ARMJIT() noexcept; + void InvalidateByAddr(u32) noexcept; + void CheckAndInvalidateWVRAM(int) noexcept; + void CheckAndInvalidateITCM() noexcept; + void Reset() noexcept; + void JitEnableWrite() noexcept; + void JitEnableExecute() noexcept; + void CompileBlock(ARM* cpu) noexcept; + void ResetBlockCache() noexcept; -extern int MaxBlockSize; -extern bool LiteralOptimizations; -extern bool BranchOptimizations; -extern bool FastMemory; + template + void CheckAndInvalidate(u32 addr) noexcept + { + u32 localAddr = Memory.LocaliseAddress(region, num, addr); + if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16))) + InvalidateByAddr(localAddr); + } + JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept; + bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept; + u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept; -void Init(); -void DeInit(); + ARMJIT_Memory Memory; +private: + int MaxBlockSize {}; + bool LiteralOptimizations = false; + bool BranchOptimizations = false; + bool FastMemory = false; +public: + melonDS::NDS& NDS; + TinyVector InvalidLiterals {}; + friend class ARMJIT_Memory; + void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept; + void RetireJitBlock(JitBlock* block) noexcept; -void Reset(); + int GetMaxBlockSize() const noexcept { return MaxBlockSize; } + bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; } + bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; } + bool FastMemoryEnabled() const noexcept { return FastMemory; } -void CheckAndInvalidateITCM(); -void CheckAndInvalidateWVRAM(int bank); + void SetJITArgs(JITArgs args) noexcept; + void SetMaxBlockSize(int size) noexcept; + void SetLiteralOptimizations(bool enabled) noexcept; + void SetBranchOptimizations(bool enabled) noexcept; + void SetFastMemory(bool enabled) noexcept; -void InvalidateByAddr(u32 pseudoPhysical); + Compiler JITCompiler; + std::unordered_map JitBlocks9 {}; + std::unordered_map JitBlocks7 {}; -template -void CheckAndInvalidate(u32 addr); + std::unordered_map RestoreCandidates {}; -void CompileBlock(ARM* cpu); -void ResetBlockCache(); + AddressRange CodeIndexITCM[ITCMPhysicalSize / 512] {}; + AddressRange CodeIndexMainRAM[MainRAMMaxSize / 512] {}; + AddressRange CodeIndexSWRAM[SharedWRAMSize / 512] {}; + AddressRange CodeIndexVRAM[0x100000 / 512] {}; + AddressRange CodeIndexARM9BIOS[ARM9BIOSSize / 512] {}; + AddressRange CodeIndexARM7BIOS[ARM7BIOSSize / 512] {}; + AddressRange CodeIndexARM7WRAM[ARM7WRAMSize / 512] {}; + AddressRange CodeIndexARM7WVRAM[0x40000 / 512] {}; + AddressRange CodeIndexBIOS9DSi[0x10000 / 512] {}; + AddressRange CodeIndexBIOS7DSi[0x10000 / 512] {}; + AddressRange CodeIndexNWRAM_A[NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_B[NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_C[NWRAMSize / 512] {}; -JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr); -bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size); + u64 FastBlockLookupITCM[ITCMPhysicalSize / 2] {}; + u64 FastBlockLookupMainRAM[MainRAMMaxSize / 2] {}; + u64 FastBlockLookupSWRAM[SharedWRAMSize / 2] {}; + u64 FastBlockLookupVRAM[0x100000 / 2] {}; + u64 FastBlockLookupARM9BIOS[ARM9BIOSSize / 2] {}; + u64 FastBlockLookupARM7BIOS[ARM7BIOSSize / 2] {}; + u64 FastBlockLookupARM7WRAM[ARM7WRAMSize / 2] {}; + u64 FastBlockLookupARM7WVRAM[0x40000 / 2] {}; + u64 FastBlockLookupBIOS9DSi[0x10000 / 2] {}; + u64 FastBlockLookupBIOS7DSi[0x10000 / 2] {}; + u64 FastBlockLookupNWRAM_A[NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_B[NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_C[NWRAMSize / 2] {}; -void JitEnableWrite(); -void JitEnableExecute(); + AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] = + { + NULL, + CodeIndexITCM, + NULL, + CodeIndexARM9BIOS, + CodeIndexMainRAM, + CodeIndexSWRAM, + NULL, + CodeIndexVRAM, + CodeIndexARM7BIOS, + CodeIndexARM7WRAM, + NULL, + NULL, + CodeIndexARM7WVRAM, + CodeIndexBIOS9DSi, + CodeIndexBIOS7DSi, + CodeIndexNWRAM_A, + CodeIndexNWRAM_B, + CodeIndexNWRAM_C + }; + + u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] = + { + NULL, + FastBlockLookupITCM, + NULL, + FastBlockLookupARM9BIOS, + FastBlockLookupMainRAM, + FastBlockLookupSWRAM, + NULL, + FastBlockLookupVRAM, + FastBlockLookupARM7BIOS, + FastBlockLookupARM7WRAM, + NULL, + NULL, + FastBlockLookupARM7WVRAM, + FastBlockLookupBIOS9DSi, + FastBlockLookupBIOS7DSi, + FastBlockLookupNWRAM_A, + FastBlockLookupNWRAM_B, + FastBlockLookupNWRAM_C + }; +}; } -extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry); +// Defined in assembly +extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry); +#else +namespace melonDS +{ +class ARM; + +// This version is a stub; the methods all do nothing, +// but there's still a Memory member. +class ARMJIT +{ +public: + ARMJIT(melonDS::NDS& nds, std::optional) noexcept : Memory(nds) {} + ~ARMJIT() noexcept {} + void InvalidateByAddr(u32) noexcept {} + void CheckAndInvalidateWVRAM(int) noexcept {} + void CheckAndInvalidateITCM() noexcept {} + void Reset() noexcept {} + void JitEnableWrite() noexcept {} + void JitEnableExecute() noexcept {} + void CompileBlock(ARM*) noexcept {} + void ResetBlockCache() noexcept {} + template + void CheckAndInvalidate(u32 addr) noexcept {} + + ARMJIT_Memory Memory; +}; +} +#endif // JIT_ENABLED + +#endif // ARMJIT_H -#endif diff --git a/src/ARMJIT_A64/ARMJIT_ALU.cpp b/src/ARMJIT_A64/ARMJIT_ALU.cpp index 3d52f280..b25bcaa3 100644 --- a/src/ARMJIT_A64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_A64/ARMJIT_ALU.cpp @@ -20,7 +20,7 @@ using namespace Arm64Gen; -namespace ARMJIT +namespace melonDS { void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs) @@ -480,7 +480,7 @@ void Compiler::A_Comp_GetOp2(bool S, Op2& op2) Comp_AddCycles_C(); u32 shift = (CurInstr.Instr >> 7) & 0x1E; - u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift); + u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift); if (S && shift && (CurInstr.SetFlags & 0x2)) { diff --git a/src/ARMJIT_A64/ARMJIT_Branch.cpp b/src/ARMJIT_A64/ARMJIT_Branch.cpp index c7b6e98e..92717e91 100644 --- a/src/ARMJIT_A64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_A64/ARMJIT_Branch.cpp @@ -17,13 +17,14 @@ */ #include "ARMJIT_Compiler.h" +#include "../NDS.h" using namespace Arm64Gen; // hack const int kCodeCacheTiming = 3; -namespace ARMJIT +namespace melonDS { template @@ -132,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1]; + cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1]; CurCPU->R[15] = compileTimePC; } @@ -144,7 +145,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3]; + cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3]; CurCPU->R[15] = compileTimePC; } @@ -235,7 +236,7 @@ void* Compiler::Gen_JumpTo7(int kind) LSR(W1, W0, 15); STR(INDEX_UNSIGNED, W1, RCPU, offsetof(ARM, CodeCycles)); - MOVP2R(X2, NDS::ARM7MemTimings); + MOVP2R(X2, NDS.ARM7MemTimings); LDR(W3, X2, ArithOption(W1, true)); FixupBranch switchToThumb; diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index 55bca846..7981ed67 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -20,6 +20,8 @@ #include "../ARMJIT_Internal.h" #include "../ARMInterpreter.h" +#include "../ARMJIT.h" +#include "../NDS.h" #if defined(__SWITCH__) #include @@ -38,7 +40,7 @@ using namespace Arm64Gen; extern "C" void ARM_Ret(); -namespace ARMJIT +namespace melonDS { /* @@ -105,7 +107,7 @@ void Compiler::A_Comp_MSR() if (CurInstr.Instr & (1 << 25)) { val = W0; - MOVI2R(val, ::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))); + MOVI2R(val, melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))); } else { @@ -219,7 +221,7 @@ void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged) } } -Compiler::Compiler() +Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds) { #ifdef __SWITCH__ JitRWBase = aligned_alloc(0x1000, JitMemSize); @@ -274,7 +276,7 @@ Compiler::Compiler() VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy); #elif defined(__APPLE__) pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0); - JitEnableWrite(); + nds.JIT.JitEnableWrite(); #else mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE); #endif @@ -704,12 +706,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] if (JitMemMainSize - GetCodeOffset() < 1024 * 16) { Log(LogLevel::Debug, "JIT near memory full, resetting...\n"); - ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8) { Log(LogLevel::Debug, "JIT far memory full, resetting...\n"); - ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } JitBlockEntry res = (JitBlockEntry)GetRXPtr(); @@ -722,7 +724,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] CPSRDirty = false; if (hasMemInstr) - MOVP2R(RMemBase, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); + MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start); for (int i = 0; i < instrsCount; i++) { @@ -870,7 +872,7 @@ void Compiler::Reset() void Compiler::Comp_AddCycles_C(bool forceNonConstant) { s32 cycles = Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles); if (forceNonConstant) @@ -884,7 +886,7 @@ void Compiler::Comp_AddCycles_CI(u32 numI) IrregularCycles = true; s32 cycles = (Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + numI; if (Thumb || CurInstr.Cond() == 0xE) @@ -898,7 +900,7 @@ void Compiler::Comp_AddCycles_CI(u32 c, ARM64Reg numI, ArithOption shift) IrregularCycles = true; s32 cycles = (Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + c; ADD(RCycles, RCycles, cycles); @@ -918,7 +920,7 @@ void Compiler::Comp_AddCycles_CDI() s32 cycles; - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM @@ -963,7 +965,7 @@ void Compiler::Comp_AddCycles_CD() } else { - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 24) == 0x02) diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 5045cb5f..2b0048a9 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -19,8 +19,9 @@ #ifndef ARMJIT_A64_COMPILER_H #define ARMJIT_A64_COMPILER_H +#if defined(JIT_ENABLED) && defined(__aarch64__) + #include "../ARM.h" -#include "../ARMJIT.h" #include "../dolphin/Arm64Emitter.h" @@ -29,9 +30,9 @@ #include -namespace ARMJIT +namespace melonDS { - +class ARMJIT; const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26; const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27; const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28; @@ -68,7 +69,7 @@ struct Op2 bool IsSimpleReg() { return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; } bool ImmFits12Bit() - { return IsImm && (Imm & 0xFFF == Imm); } + { return IsImm && ((Imm & 0xFFF) == Imm); } bool IsZero() { return IsImm && !Imm; } @@ -97,8 +98,8 @@ class Compiler : public Arm64Gen::ARM64XEmitter public: typedef void (Compiler::*CompileFunc)(); - Compiler(); - ~Compiler(); + explicit Compiler(melonDS::NDS& nds); + ~Compiler() override; void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true); void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged); @@ -113,7 +114,7 @@ public: bool CanCompile(bool thumb, u16 kind); - bool FlagsNZNeeded() + bool FlagsNZNeeded() const { return CurInstr.SetFlags & 0xC; } @@ -233,7 +234,7 @@ public: return (u8*)entry - GetRXBase(); } - bool IsJITFault(u8* pc); + bool IsJITFault(const u8* pc); u8* RewriteMemAccess(u8* pc); void SwapCodeRegion() @@ -243,6 +244,7 @@ public: OtherCodeRegion = offset; } + melonDS::NDS& NDS; ptrdiff_t OtherCodeRegion; bool Exit; @@ -287,3 +289,5 @@ public: } #endif + +#endif diff --git a/src/ARMJIT_A64/ARMJIT_Linkage.S b/src/ARMJIT_A64/ARMJIT_Linkage.S index c1ecd7c8..b73905bd 100644 --- a/src/ARMJIT_A64/ARMJIT_Linkage.S +++ b/src/ARMJIT_A64/ARMJIT_Linkage.S @@ -94,3 +94,8 @@ ARM_RestoreContext: mov sp, x17 br x18 + +#if !defined(__APPLE__) && !defined(__WIN32__) +.section .note.GNU-stack,"",@progbits +#endif + diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index ee8aabe2..e108b7b4 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -21,13 +21,14 @@ #include "../ARMJIT.h" #include "../ARMJIT_Memory.h" +#include "../NDS.h" using namespace Arm64Gen; -namespace ARMJIT +namespace melonDS { -bool Compiler::IsJITFault(u8* pc) +bool Compiler::IsJITFault(const u8* pc) { return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize); } @@ -62,9 +63,9 @@ u8* Compiler::RewriteMemAccess(u8* pc) bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) { - u32 localAddr = LocaliseCodeAddress(Num, addr); + u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr); - int invalidLiteralIdx = InvalidLiterals.Find(localAddr); + int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr); if (invalidLiteralIdx != -1) { return false; @@ -79,7 +80,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) if (size == 32) { CurCPU->DataRead32(addr & ~0x3, &val); - val = ::ROR(val, (addr & 0x3) << 3); + val = melonDS::ROR(val, (addr & 0x3) << 3); } else if (size == 16) { @@ -111,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (size == 16) addressMask = ~1; - if (ARMJIT::LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -146,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(W0, rnMapped); } - bool addrIsStatic = ARMJIT::LiteralOptimizations + bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled() && RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -185,18 +186,18 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(rnMapped, W0); u32 expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); - if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget))) + if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) { ptrdiff_t memopStart = GetCodeOffset(); LoadStorePatch patch; assert((rdMapped >= W8 && rdMapped <= W15) || (rdMapped >= W19 && rdMapped <= W25) || rdMapped == W4); patch.PatchFunc = flags & memop_Store - ? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped] - : PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped]; + ? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped] + : PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped]; // take a chance at fastmem if (size > 8) @@ -225,7 +226,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) { void* func = NULL; if (addrIsStatic) - func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); + func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); PushRegs(false, false); @@ -263,7 +264,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (flags & memop_Store) { MOV(W2, rdMapped); - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowWrite9); break; case 33: QuickCallFunction(X3, SlowWrite9); break; @@ -275,7 +276,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowRead9); break; case 33: QuickCallFunction(X3, SlowRead9); break; @@ -291,7 +292,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (flags & memop_Store) { MOV(W1, rdMapped); - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowWrite7); break; case 33: QuickCallFunction(X3, SlowWrite7); break; @@ -303,7 +304,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowRead7); break; case 33: QuickCallFunction(X3, SlowRead7); break; @@ -452,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel() u32 offset = ((CurInstr.Instr & 0xFF) << 2); u32 addr = (R15 & ~0x2) + offset; - if (!ARMJIT::LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } @@ -494,11 +495,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc Comp_AddCycles_CDI(); int expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - bool compileFastPath = ARMJIT::FastMemory - && store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)); + bool compileFastPath = NDS.JIT.FastMemoryEnabled() + && store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)); { s32 offset = decrement @@ -680,7 +681,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc if (Num == 0) { MOV(X3, RCPU); - switch ((u32)store * 2 | NDS::ConsoleType) + switch ((u32)store * 2 | NDS.ConsoleType) { case 0: QuickCallFunction(X4, SlowBlockTransfer9); break; case 1: QuickCallFunction(X4, SlowBlockTransfer9); break; @@ -690,7 +691,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc } else { - switch ((u32)store * 2 | NDS::ConsoleType) + switch ((u32)store * 2 | NDS.ConsoleType) { case 0: QuickCallFunction(X4, SlowBlockTransfer7); break; case 1: QuickCallFunction(X4, SlowBlockTransfer7); break; diff --git a/src/ARMJIT_Compiler.h b/src/ARMJIT_Compiler.h index c5348f48..ff4f8ff7 100644 --- a/src/ARMJIT_Compiler.h +++ b/src/ARMJIT_Compiler.h @@ -19,6 +19,8 @@ #ifndef ARMJIT_COMPILER_H #define ARMJIT_COMPILER_H +#ifdef JIT_ENABLED + #if defined(__x86_64__) #include "ARMJIT_x64/ARMJIT_Compiler.h" #elif defined(__aarch64__) @@ -27,9 +29,6 @@ #error "The current target platform doesn't have a JIT backend" #endif -namespace ARMJIT -{ -extern Compiler* JITCompiler; -} +#endif #endif diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h index fb803072..8429bade 100644 --- a/src/ARMJIT_Internal.h +++ b/src/ARMJIT_Internal.h @@ -24,13 +24,17 @@ #include #include -#include "ARMJIT.h" -#include "ARMJIT_Memory.h" +#include "ARM_InstrInfo.h" +#include "JitBlock.h" +#include "TinyVector.h" + +namespace melonDS +{ +class ARM; +class ARMv5; // here lands everything which doesn't fit into ARMJIT.h // where it would be included by pretty much everything -namespace ARMJIT -{ enum { @@ -69,139 +73,6 @@ struct FetchedInstr ARMInstrInfo::Info Info; }; -/* - TinyVector - - because reinventing the wheel is the best! - - - meant to be used very often, with not so many elements - max 1 << 16 elements - - doesn't allocate while no elements are inserted - - not stl confirmant of course - - probably only works with POD types - - remove operations don't preserve order, but O(1)! -*/ -template -struct __attribute__((packed)) TinyVector -{ - T* Data = NULL; - u16 Capacity = 0; - u16 Length = 0; - - ~TinyVector() - { - delete[] Data; - } - - void MakeCapacity(u32 capacity) - { - assert(capacity <= UINT16_MAX); - assert(capacity > Capacity); - T* newMem = new T[capacity]; - if (Data != NULL) - memcpy(newMem, Data, sizeof(T) * Length); - - T* oldData = Data; - Data = newMem; - if (oldData != NULL) - delete[] oldData; - - Capacity = capacity; - } - - void SetLength(u16 length) - { - if (Capacity < length) - MakeCapacity(length); - - Length = length; - } - - void Clear() - { - Length = 0; - } - - void Add(T element) - { - assert(Length + 1 <= UINT16_MAX); - if (Length + 1 > Capacity) - MakeCapacity(((Capacity + 4) * 3) / 2); - - Data[Length++] = element; - } - - void Remove(int index) - { - assert(Length > 0); - assert(index >= 0 && index < Length); - - Length--; - Data[index] = Data[Length]; - /*for (int i = index; i < Length; i++) - Data[i] = Data[i + 1];*/ - } - - int Find(T needle) - { - for (int i = 0; i < Length; i++) - { - if (Data[i] == needle) - return i; - } - return -1; - } - - bool RemoveByValue(T needle) - { - for (int i = 0; i < Length; i++) - { - if (Data[i] == needle) - { - Remove(i); - return true; - } - } - return false; - } - - T& operator[](int index) - { - assert(index >= 0 && index < Length); - return Data[index]; - } -}; - -class JitBlock -{ -public: - JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals) - { - Num = num; - NumAddresses = numAddresses; - NumLiterals = numLiterals; - Data.SetLength(numAddresses * 2 + numLiterals); - } - - u32 StartAddr; - u32 StartAddrLocal; - u32 InstrHash, LiteralHash; - u8 Num; - u16 NumAddresses; - u16 NumLiterals; - - JitBlockEntry EntryPoint; - - u32* AddressRanges() - { return &Data[0]; } - u32* AddressMasks() - { return &Data[NumAddresses]; } - u32* Literals() - { return &Data[NumAddresses * 2]; } - -private: - TinyVector Data; -}; - // size should be 16 bytes because I'm to lazy to use mul and whatnot struct __attribute__((packed)) AddressRange { @@ -214,11 +85,7 @@ typedef void (*InterpreterFunc)(ARM* cpu); extern InterpreterFunc InterpretARM[]; extern InterpreterFunc InterpretTHUMB[]; -extern TinyVector InvalidLiterals; - -extern AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count]; - -inline bool PageContainsCode(AddressRange* range) +inline bool PageContainsCode(const AddressRange* range) { for (int i = 0; i < 8; i++) { @@ -228,11 +95,6 @@ inline bool PageContainsCode(AddressRange* range) return false; } -u32 LocaliseCodeAddress(u32 num, u32 addr); - -template -void LinkBlock(ARM* cpu, u32 codeOffset); - template T SlowRead9(u32 addr, ARMv5* cpu); template void SlowWrite9(u32 addr, ARMv5* cpu, u32 val); template T SlowRead7(u32 addr); diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 25652cb8..c8969aee 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -34,6 +34,7 @@ #include #endif +#include "ARMJIT.h" #include "ARMJIT_Memory.h" #include "ARMJIT_Internal.h" @@ -48,9 +49,6 @@ #include -using Platform::Log; -using Platform::LogLevel; - /* We're handling fastmem here. @@ -72,17 +70,6 @@ using Platform::LogLevel; */ -namespace ARMJIT_Memory -{ -struct FaultDescription -{ - u32 EmulatedFaultAddr; - u8* FaultPC; -}; - -bool FaultHandler(FaultDescription& faultDesc); -} - // Yes I know this looks messy, but better here than somewhere else in the code #if defined(__x86_64__) #if defined(_WIN32) @@ -110,9 +97,14 @@ bool FaultHandler(FaultDescription& faultDesc); #endif #endif +namespace melonDS +{ + +using Platform::Log; +using Platform::LogLevel; + #if defined(__ANDROID__) #define ASHMEM_DEVICE "/dev/ashmem" -Platform::DynamicLibrary* Libandroid = nullptr; #endif #if defined(__SWITCH__) @@ -146,7 +138,7 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx) integerRegisters[31] = ctx->sp.x; integerRegisters[32] = ctx->pc.x; - if (ARMJIT_Memory::FaultHandler(desc)) + if (Melon::FaultHandler(desc)) { integerRegisters[32] = (u64)desc.FaultPC; @@ -160,19 +152,19 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx) #elif defined(_WIN32) -static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) +LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) { if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { return EXCEPTION_CONTINUE_SEARCH; } - ARMJIT_Memory::FaultDescription desc; - u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); + u8* curArea = (u8*)(NDS::Current->CurCPU == 0 ? NDS::Current->JIT.Memory.FastMem9Start : NDS::Current->JIT.Memory.FastMem7Start); + FaultDescription desc {}; desc.EmulatedFaultAddr = (u8*)exceptionInfo->ExceptionRecord->ExceptionInformation[1] - curArea; desc.FaultPC = (u8*)exceptionInfo->ContextRecord->CONTEXT_PC; - if (ARMJIT_Memory::FaultHandler(desc)) + if (FaultHandler(desc, *NDS::Current)) { exceptionInfo->ContextRecord->CONTEXT_PC = (u64)desc.FaultPC; return EXCEPTION_CONTINUE_EXECUTION; @@ -186,7 +178,7 @@ static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) static struct sigaction OldSaSegv; static struct sigaction OldSaBus; -static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext) +void ARMJIT_Memory::SigsegvHandler(int sig, siginfo_t* info, void* rawContext) { if (sig != SIGSEGV && sig != SIGBUS) { @@ -201,13 +193,13 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext) ucontext_t* context = (ucontext_t*)rawContext; - ARMJIT_Memory::FaultDescription desc; - u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); + FaultDescription desc {}; + u8* curArea = (u8*)(NDS::Current->CurCPU == 0 ? NDS::Current->JIT.Memory.FastMem9Start : NDS::Current->JIT.Memory.FastMem7Start); desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea; desc.FaultPC = (u8*)context->CONTEXT_PC; - if (ARMJIT_Memory::FaultHandler(desc)) + if (FaultHandler(desc, *NDS::Current)) { context->CONTEXT_PC = (u64)desc.FaultPC; return; @@ -239,33 +231,7 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext) #endif -namespace ARMJIT_Memory -{ - -void* FastMem9Start, *FastMem7Start; - -#ifdef _WIN32 -inline u32 RoundUp(u32 size) -{ - return (size + 0xFFFF) & ~0xFFFF; -} -#else -inline u32 RoundUp(u32 size) -{ - return size; -} -#endif - -const u32 MemBlockMainRAMOffset = 0; -const u32 MemBlockSWRAMOffset = RoundUp(NDS::MainRAMMaxSize); -const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(NDS::SharedWRAMSize); -const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(NDS::ARM7WRAMSize); -const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); -const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(DSi::NWRAMSize); -const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(DSi::NWRAMSize); -const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(DSi::NWRAMSize); - -const u32 OffsetsPerRegion[memregions_Count] = +const u32 OffsetsPerRegion[ARMJIT_Memory::memregions_Count] = { UINT32_MAX, UINT32_MAX, @@ -295,23 +261,9 @@ enum memstate_MappedProtected, }; -u8 MappingStatus9[1 << (32-12)]; -u8 MappingStatus7[1 << (32-12)]; -#if defined(__SWITCH__) -VirtmemReservation* FastMem9Reservation, *FastMem7Reservation; -u8* MemoryBase; -u8* MemoryBaseCodeMem; -#elif defined(_WIN32) -u8* MemoryBase; -HANDLE MemoryFile; -LPVOID ExceptionHandlerHandle; -#else -u8* MemoryBase; -int MemoryFile = -1; -#endif -bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) +bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept { u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #ifdef __SWITCH__ @@ -326,7 +278,7 @@ bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) #endif } -bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) +bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept { u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #ifdef __SWITCH__ @@ -341,7 +293,7 @@ bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) } #ifndef __SWITCH__ -void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) +void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept { u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #if defined(_WIN32) @@ -367,82 +319,74 @@ void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) } #endif -struct Mapping +void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept { - u32 Addr; - u32 Size, LocalOffset; - u32 Num; - - void Unmap(int region) + u32 dtcmStart = nds.ARM9.DTCMBase; + u32 dtcmSize = ~nds.ARM9.DTCMMask + 1; + bool skipDTCM = Num == 0 && region != memregion_DTCM; + u8* statuses = Num == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7; + u32 offset = 0; + while (offset < Size) { - u32 dtcmStart = NDS::ARM9->DTCMBase; - u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; - bool skipDTCM = Num == 0 && region != memregion_DTCM; - u8* statuses = Num == 0 ? MappingStatus9 : MappingStatus7; - u32 offset = 0; - while (offset < Size) + if (skipDTCM && Addr + offset == dtcmStart) { - if (skipDTCM && Addr + offset == dtcmStart) + offset += dtcmSize; + } + else + { + u32 segmentOffset = offset; + u8 status = statuses[(Addr + offset) >> 12]; + while (statuses[(Addr + offset) >> 12] == status + && offset < Size + && (!skipDTCM || Addr + offset != dtcmStart)) { - offset += dtcmSize; + assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped); + statuses[(Addr + offset) >> 12] = memstate_Unmapped; + offset += 0x1000; } - else - { - u32 segmentOffset = offset; - u8 status = statuses[(Addr + offset) >> 12]; - while (statuses[(Addr + offset) >> 12] == status - && offset < Size - && (!skipDTCM || Addr + offset != dtcmStart)) - { - assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped); - statuses[(Addr + offset) >> 12] = memstate_Unmapped; - offset += 0x1000; - } #ifdef __SWITCH__ - if (status == memstate_MappedRW) - { - u32 segmentSize = offset - segmentOffset; - Log(LogLevel::Debug, "unmapping %x %x %x %x\n", Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); - bool success = UnmapFromRange(Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); - assert(success); - } -#endif + if (status == memstate_MappedRW) + { + u32 segmentSize = offset - segmentOffset; + Log(LogLevel::Debug, "unmapping %x %x %x %x\n", Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); + bool success = memory.UnmapFromRange(Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); + assert(success); } +#endif } + } #ifndef __SWITCH__ #ifndef _WIN32 - u32 dtcmEnd = dtcmStart + dtcmSize; - if (Num == 0 - && dtcmEnd >= Addr - && dtcmStart < Addr + Size) + u32 dtcmEnd = dtcmStart + dtcmSize; + if (Num == 0 + && dtcmEnd >= Addr + && dtcmStart < Addr + Size) + { + bool success; + if (dtcmStart > Addr) { - bool success; - if (dtcmStart > Addr) - { - success = UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr); - assert(success); - } - if (dtcmEnd < Addr + Size) - { - u32 offset = dtcmStart - Addr + dtcmSize; - success = UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset); - assert(success); - } + success = nds.JIT.Memory.UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr); + assert(success); } - else -#endif + if (dtcmEnd < Addr + Size) { - bool succeded = UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); - assert(succeded); + u32 offset = dtcmStart - Addr + dtcmSize; + success = nds.JIT.Memory.UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset); + assert(success); } -#endif } -}; -ARMJIT::TinyVector Mappings[memregions_Count]; + else +#endif + { + bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); + assert(succeded); + } +#endif +} -void SetCodeProtection(int region, u32 offset, bool protect) +void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept { offset &= ~0xFFF; //printf("set code protection %d %x %d\n", region, offset, protect); @@ -457,7 +401,7 @@ void SetCodeProtection(int region, u32 offset, bool protect) u32 effectiveAddr = mapping.Addr + (offset - mapping.LocalOffset); if (mapping.Num == 0 && region != memregion_DTCM - && (effectiveAddr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase) + && (effectiveAddr & NDS.ARM9.DTCMMask) == NDS.ARM9.DTCMBase) continue; u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7); @@ -479,13 +423,13 @@ void SetCodeProtection(int region, u32 offset, bool protect) } } -void RemapDTCM(u32 newBase, u32 newSize) +void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept { // this first part could be made more efficient // by unmapping DTCM first and then map the holes - u32 oldDTCMBase = NDS::ARM9->DTCMBase; - u32 oldDTCMSize = ~NDS::ARM9->DTCMMask + 1; - u32 oldDTCMEnd = oldDTCMBase + NDS::ARM9->DTCMMask; + u32 oldDTCMBase = NDS.ARM9.DTCMBase; + u32 oldDTCMSize = ~NDS.ARM9.DTCMMask + 1; + u32 oldDTCMEnd = oldDTCMBase + NDS.ARM9.DTCMMask; u32 newEnd = newBase + newSize; @@ -510,7 +454,7 @@ void RemapDTCM(u32 newBase, u32 newSize) if (mapping.Num == 0 && overlap) { - mapping.Unmap(region); + mapping.Unmap(region, NDS); Mappings[region].Remove(i); } else @@ -522,20 +466,24 @@ void RemapDTCM(u32 newBase, u32 newSize) for (int i = 0; i < Mappings[memregion_DTCM].Length; i++) { - Mappings[memregion_DTCM][i].Unmap(memregion_DTCM); + Mappings[memregion_DTCM][i].Unmap(memregion_DTCM, NDS); } Mappings[memregion_DTCM].Clear(); } -void RemapNWRAM(int num) +void ARMJIT_Memory::RemapNWRAM(int num) noexcept { + if (NDS.ConsoleType == 0) + return; + + auto* dsi = static_cast(&NDS); for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;) { Mapping& mapping = Mappings[memregion_SharedWRAM][i]; - if (DSi::NWRAMStart[mapping.Num][num] < mapping.Addr + mapping.Size - && DSi::NWRAMEnd[mapping.Num][num] > mapping.Addr) + if (dsi->NWRAMStart[mapping.Num][num] < mapping.Addr + mapping.Size + && dsi->NWRAMEnd[mapping.Num][num] > mapping.Addr) { - mapping.Unmap(memregion_SharedWRAM); + mapping.Unmap(memregion_SharedWRAM, NDS); Mappings[memregion_SharedWRAM].Remove(i); } else @@ -545,12 +493,12 @@ void RemapNWRAM(int num) } for (int i = 0; i < Mappings[memregion_NewSharedWRAM_A + num].Length; i++) { - Mappings[memregion_NewSharedWRAM_A + num][i].Unmap(memregion_NewSharedWRAM_A + num); + Mappings[memregion_NewSharedWRAM_A + num][i].Unmap(memregion_NewSharedWRAM_A + num, NDS); } Mappings[memregion_NewSharedWRAM_A + num].Clear(); } -void RemapSWRAM() +void ARMJIT_Memory::RemapSWRAM() noexcept { Log(LogLevel::Debug, "remapping SWRAM\n"); for (int i = 0; i < Mappings[memregion_WRAM7].Length;) @@ -558,7 +506,7 @@ void RemapSWRAM() Mapping& mapping = Mappings[memregion_WRAM7][i]; if (mapping.Addr + mapping.Size <= 0x03800000) { - mapping.Unmap(memregion_WRAM7); + mapping.Unmap(memregion_WRAM7, NDS); Mappings[memregion_WRAM7].Remove(i); } else @@ -566,14 +514,14 @@ void RemapSWRAM() } for (int i = 0; i < Mappings[memregion_SharedWRAM].Length; i++) { - Mappings[memregion_SharedWRAM][i].Unmap(memregion_SharedWRAM); + Mappings[memregion_SharedWRAM][i].Unmap(memregion_SharedWRAM, NDS); } Mappings[memregion_SharedWRAM].Clear(); } -bool MapAtAddress(u32 addr) +bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept { - u32 num = NDS::CurCPU; + u32 num = NDS.CurCPU; int region = num == 0 ? ClassifyAddress9(addr) @@ -589,10 +537,10 @@ bool MapAtAddress(u32 addr) u8* states = num == 0 ? MappingStatus9 : MappingStatus7; //printf("mapping mirror %x, %x %x %d %d\n", mirrorStart, mirrorSize, memoryOffset, region, num); - bool isExecutable = ARMJIT::CodeMemRegions[region]; + bool isExecutable = NDS.JIT.CodeMemRegions[region]; - u32 dtcmStart = NDS::ARM9->DTCMBase; - u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; + u32 dtcmStart = NDS.ARM9.DTCMBase; + u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1; u32 dtcmEnd = dtcmStart + dtcmSize; #ifndef __SWITCH__ #ifndef _WIN32 @@ -621,7 +569,7 @@ bool MapAtAddress(u32 addr) } #endif - ARMJIT::AddressRange* range = ARMJIT::CodeMemRegions[region] + memoryOffset / 512; + AddressRange* range = NDS.JIT.CodeMemRegions[region] + memoryOffset / 512; // this overcomplicated piece of code basically just finds whole pieces of code memory // which can be mapped/protected @@ -639,10 +587,10 @@ bool MapAtAddress(u32 addr) else { u32 sectionOffset = offset; - bool hasCode = isExecutable && ARMJIT::PageContainsCode(&range[offset / 512]); + bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]); while (offset < mirrorSize - && (!isExecutable || ARMJIT::PageContainsCode(&range[offset / 512]) == hasCode) - && (!skipDTCM || mirrorStart + offset != NDS::ARM9->DTCMBase)) + && (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode) + && (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase)) { assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped); states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW; @@ -676,19 +624,19 @@ bool MapAtAddress(u32 addr) return true; } -bool FaultHandler(FaultDescription& faultDesc) +bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds) { - if (ARMJIT::JITCompiler->IsJITFault(faultDesc.FaultPC)) + if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC)) { bool rewriteToSlowPath = true; - u8* memStatus = NDS::CurCPU == 0 ? MappingStatus9 : MappingStatus7; + u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7; if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped) - rewriteToSlowPath = !MapAtAddress(faultDesc.EmulatedFaultAddr); + rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr); if (rewriteToSlowPath) - faultDesc.FaultPC = ARMJIT::JITCompiler->RewriteMemAccess(faultDesc.FaultPC); + faultDesc.FaultPC = nds.JIT.JITCompiler.RewriteMemAccess(faultDesc.FaultPC); return true; } @@ -697,7 +645,7 @@ bool FaultHandler(FaultDescription& faultDesc) const u64 AddrSpaceSize = 0x100000000; -void Init() +ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds) { #if defined(__SWITCH__) MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize); @@ -740,8 +688,6 @@ void Init() MemoryBase = MemoryBase + AddrSpaceSize*3; MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, MemoryTotalSize, MemoryBase); - - u8* basePtr = MemoryBase; #else // this used to be allocated with three different mmaps // The idea was to give the OS more freedom where to position the buffers, @@ -798,16 +744,9 @@ void Init() u8* basePtr = MemoryBase; #endif - NDS::MainRAM = basePtr + MemBlockMainRAMOffset; - NDS::SharedWRAM = basePtr + MemBlockSWRAMOffset; - NDS::ARM7WRAM = basePtr + MemBlockARM7WRAMOffset; - NDS::ARM9->DTCM = basePtr + MemBlockDTCMOffset; - DSi::NWRAM_A = basePtr + MemBlockNWRAM_AOffset; - DSi::NWRAM_B = basePtr + MemBlockNWRAM_BOffset; - DSi::NWRAM_C = basePtr + MemBlockNWRAM_COffset; } -void DeInit() +ARMJIT_Memory::~ARMJIT_Memory() noexcept { #if defined(__SWITCH__) virtmemLock(); @@ -875,12 +814,12 @@ void DeInit() #endif } -void Reset() +void ARMJIT_Memory::Reset() noexcept { for (int region = 0; region < memregions_Count; region++) { for (int i = 0; i < Mappings[region].Length; i++) - Mappings[region][i].Unmap(region); + Mappings[region][i].Unmap(region, NDS); Mappings[region].Clear(); } @@ -893,7 +832,7 @@ void Reset() Log(LogLevel::Debug, "done resetting jit mem\n"); } -bool IsFastmemCompatible(int region) +bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept { #ifdef _WIN32 /* @@ -909,7 +848,7 @@ bool IsFastmemCompatible(int region) return OffsetsPerRegion[region] != UINT32_MAX; } -bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) +bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept { memoryOffset = 0; switch (region) @@ -931,8 +870,8 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi } return false; case memregion_MainRAM: - mirrorStart = addr & ~NDS::MainRAMMask; - mirrorSize = NDS::MainRAMMask + 1; + mirrorStart = addr & ~NDS.MainRAMMask; + mirrorSize = NDS.MainRAMMask + 1; return true; case memregion_BIOS9: if (num == 0) @@ -951,26 +890,26 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi } return false; case memregion_SharedWRAM: - if (num == 0 && NDS::SWRAM_ARM9.Mem) + if (num == 0 && NDS.SWRAM_ARM9.Mem) { - mirrorStart = addr & ~NDS::SWRAM_ARM9.Mask; - mirrorSize = NDS::SWRAM_ARM9.Mask + 1; - memoryOffset = NDS::SWRAM_ARM9.Mem - NDS::SharedWRAM; + mirrorStart = addr & ~NDS.SWRAM_ARM9.Mask; + mirrorSize = NDS.SWRAM_ARM9.Mask + 1; + memoryOffset = NDS.SWRAM_ARM9.Mem - GetSharedWRAM(); return true; } - else if (num == 1 && NDS::SWRAM_ARM7.Mem) + else if (num == 1 && NDS.SWRAM_ARM7.Mem) { - mirrorStart = addr & ~NDS::SWRAM_ARM7.Mask; - mirrorSize = NDS::SWRAM_ARM7.Mask + 1; - memoryOffset = NDS::SWRAM_ARM7.Mem - NDS::SharedWRAM; + mirrorStart = addr & ~NDS.SWRAM_ARM7.Mask; + mirrorSize = NDS.SWRAM_ARM7.Mask + 1; + memoryOffset = NDS.SWRAM_ARM7.Mem - GetSharedWRAM(); return true; } return false; case memregion_WRAM7: if (num == 1) { - mirrorStart = addr & ~(NDS::ARM7WRAMSize - 1); - mirrorSize = NDS::ARM7WRAMSize; + mirrorStart = addr & ~(ARM7WRAMSize - 1); + mirrorSize = ARM7WRAMSize; return true; } return false; @@ -992,10 +931,12 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi return false; case memregion_NewSharedWRAM_A: { - u8* ptr = DSi::NWRAMMap_A[num][(addr >> 16) & DSi::NWRAMMask[num][0]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_A[num][(addr >> 16) & dsi->NWRAMMask[num][0]]; if (ptr) { - memoryOffset = ptr - DSi::NWRAM_A; + memoryOffset = ptr - GetNWRAM_A(); mirrorStart = addr & ~0xFFFF; mirrorSize = 0x10000; return true; @@ -1004,10 +945,12 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi } case memregion_NewSharedWRAM_B: { - u8* ptr = DSi::NWRAMMap_B[num][(addr >> 15) & DSi::NWRAMMask[num][1]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_B[num][(addr >> 15) & dsi->NWRAMMask[num][1]]; if (ptr) { - memoryOffset = ptr - DSi::NWRAM_B; + memoryOffset = ptr - GetNWRAM_B(); mirrorStart = addr & ~0x7FFF; mirrorSize = 0x8000; return true; @@ -1016,10 +959,12 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi } case memregion_NewSharedWRAM_C: { - u8* ptr = DSi::NWRAMMap_C[num][(addr >> 15) & DSi::NWRAMMask[num][2]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_C[num][(addr >> 15) & dsi->NWRAMMask[num][2]]; if (ptr) { - memoryOffset = ptr - DSi::NWRAM_C; + memoryOffset = ptr - GetNWRAM_C(); mirrorStart = addr & ~0x7FFF; mirrorSize = 0x8000; return true; @@ -1029,16 +974,20 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi case memregion_BIOS9DSi: if (num == 0) { + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); mirrorStart = addr & ~0xFFFF; - mirrorSize = DSi::SCFG_BIOS & (1<<0) ? 0x8000 : 0x10000; + mirrorSize = dsi->SCFG_BIOS & (1<<0) ? 0x8000 : 0x10000; return true; } return false; case memregion_BIOS7DSi: if (num == 1) { + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); mirrorStart = addr & ~0xFFFF; - mirrorSize = DSi::SCFG_BIOS & (1<<8) ? 0x8000 : 0x10000; + mirrorSize = dsi->SCFG_BIOS & (1<<8) ? 0x8000 : 0x10000; return true; } return false; @@ -1048,25 +997,25 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi } } -u32 LocaliseAddress(int region, u32 num, u32 addr) +u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept { switch (region) { case memregion_ITCM: return (addr & (ITCMPhysicalSize - 1)) | (memregion_ITCM << 27); case memregion_MainRAM: - return (addr & NDS::MainRAMMask) | (memregion_MainRAM << 27); + return (addr & NDS.MainRAMMask) | (memregion_MainRAM << 27); case memregion_BIOS9: return (addr & 0xFFF) | (memregion_BIOS9 << 27); case memregion_BIOS7: return (addr & 0x3FFF) | (memregion_BIOS7 << 27); case memregion_SharedWRAM: if (num == 0) - return ((addr & NDS::SWRAM_ARM9.Mask) + (NDS::SWRAM_ARM9.Mem - NDS::SharedWRAM)) | (memregion_SharedWRAM << 27); + return ((addr & NDS.SWRAM_ARM9.Mask) + (NDS.SWRAM_ARM9.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); else - return ((addr & NDS::SWRAM_ARM7.Mask) + (NDS::SWRAM_ARM7.Mem - NDS::SharedWRAM)) | (memregion_SharedWRAM << 27); + return ((addr & NDS.SWRAM_ARM7.Mask) + (NDS.SWRAM_ARM7.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); case memregion_WRAM7: - return (addr & (NDS::ARM7WRAMSize - 1)) | (memregion_WRAM7 << 27); + return (addr & (melonDS::ARM7WRAMSize - 1)) | (memregion_WRAM7 << 27); case memregion_VRAM: // TODO: take mapping properly into account return (addr & 0xFFFFF) | (memregion_VRAM << 27); @@ -1075,25 +1024,31 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) return (addr & 0x3FFFF) | (memregion_VWRAM << 27); case memregion_NewSharedWRAM_A: { - u8* ptr = DSi::NWRAMMap_A[num][(addr >> 16) & DSi::NWRAMMask[num][0]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_A[num][(addr >> 16) & dsi->NWRAMMask[num][0]]; if (ptr) - return (ptr - DSi::NWRAM_A + (addr & 0xFFFF)) | (memregion_NewSharedWRAM_A << 27); + return (ptr - GetNWRAM_A() + (addr & 0xFFFF)) | (memregion_NewSharedWRAM_A << 27); else return memregion_Other << 27; // zero filled memory } case memregion_NewSharedWRAM_B: { - u8* ptr = DSi::NWRAMMap_B[num][(addr >> 15) & DSi::NWRAMMask[num][1]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_B[num][(addr >> 15) & dsi->NWRAMMask[num][1]]; if (ptr) - return (ptr - DSi::NWRAM_B + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_B << 27); + return (ptr - GetNWRAM_B() + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_B << 27); else return memregion_Other << 27; } case memregion_NewSharedWRAM_C: { - u8* ptr = DSi::NWRAMMap_C[num][(addr >> 15) & DSi::NWRAMMask[num][2]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_C[num][(addr >> 15) & dsi->NWRAMMask[num][2]]; if (ptr) - return (ptr - DSi::NWRAM_C + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_C << 27); + return (ptr - GetNWRAM_C() + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_C << 27); else return memregion_Other << 27; } @@ -1106,26 +1061,31 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) } } -int ClassifyAddress9(u32 addr) +int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept { - if (addr < NDS::ARM9->ITCMSize) + if (addr < NDS.ARM9.ITCMSize) { return memregion_ITCM; } - else if ((addr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase) + else if ((addr & NDS.ARM9.DTCMMask) == NDS.ARM9.DTCMBase) { return memregion_DTCM; } else { - if (NDS::ConsoleType == 1 && addr >= 0xFFFF0000 && !(DSi::SCFG_BIOS & (1<<1))) + if (NDS.ConsoleType == 1) { - if ((addr >= 0xFFFF8000) && (DSi::SCFG_BIOS & (1<<0))) - return memregion_Other; + auto& dsi = static_cast(NDS); + if (addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1))) + { + if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0))) + return memregion_Other; - return memregion_BIOS9DSi; + return memregion_BIOS9DSi; + } } - else if ((addr & 0xFFFFF000) == 0xFFFF0000) + + if ((addr & 0xFFFFF000) == 0xFFFF0000) { return memregion_BIOS9; } @@ -1135,17 +1095,18 @@ int ClassifyAddress9(u32 addr) case 0x02000000: return memregion_MainRAM; case 0x03000000: - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { - if (addr >= DSi::NWRAMStart[0][0] && addr < DSi::NWRAMEnd[0][0]) + auto& dsi = static_cast(NDS); + if (addr >= dsi.NWRAMStart[0][0] && addr < dsi.NWRAMEnd[0][0]) return memregion_NewSharedWRAM_A; - if (addr >= DSi::NWRAMStart[0][1] && addr < DSi::NWRAMEnd[0][1]) + if (addr >= dsi.NWRAMStart[0][1] && addr < dsi.NWRAMEnd[0][1]) return memregion_NewSharedWRAM_B; - if (addr >= DSi::NWRAMStart[0][2] && addr < DSi::NWRAMEnd[0][2]) + if (addr >= dsi.NWRAMStart[0][2] && addr < dsi.NWRAMEnd[0][2]) return memregion_NewSharedWRAM_C; } - if (NDS::SWRAM_ARM9.Mem) + if (NDS.SWRAM_ARM9.Mem) return memregion_SharedWRAM; return memregion_Other; case 0x04000000: @@ -1153,23 +1114,28 @@ int ClassifyAddress9(u32 addr) case 0x06000000: return memregion_VRAM; case 0x0C000000: - return (NDS::ConsoleType==1) ? memregion_MainRAM : memregion_Other; + return (NDS.ConsoleType==1) ? memregion_MainRAM : memregion_Other; default: return memregion_Other; } } } -int ClassifyAddress7(u32 addr) +int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept { - if (NDS::ConsoleType == 1 && addr < 0x00010000 && !(DSi::SCFG_BIOS & (1<<9))) + if (NDS.ConsoleType == 1) { - if (addr >= 0x00008000 && DSi::SCFG_BIOS & (1<<8)) - return memregion_Other; + auto& dsi = static_cast(NDS); + if (addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9))) + { + if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8)) + return memregion_Other; - return memregion_BIOS7DSi; + return memregion_BIOS7DSi; + } } - else if (addr < 0x00004000) + + if (addr < 0x00004000) { return memregion_BIOS7; } @@ -1181,17 +1147,18 @@ int ClassifyAddress7(u32 addr) case 0x02800000: return memregion_MainRAM; case 0x03000000: - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { - if (addr >= DSi::NWRAMStart[1][0] && addr < DSi::NWRAMEnd[1][0]) + auto& dsi = static_cast(NDS); + if (addr >= dsi.NWRAMStart[1][0] && addr < dsi.NWRAMEnd[1][0]) return memregion_NewSharedWRAM_A; - if (addr >= DSi::NWRAMStart[1][1] && addr < DSi::NWRAMEnd[1][1]) + if (addr >= dsi.NWRAMStart[1][1] && addr < dsi.NWRAMEnd[1][1]) return memregion_NewSharedWRAM_B; - if (addr >= DSi::NWRAMStart[1][2] && addr < DSi::NWRAMEnd[1][2]) + if (addr >= dsi.NWRAMStart[1][2] && addr < dsi.NWRAMEnd[1][2]) return memregion_NewSharedWRAM_C; } - if (NDS::SWRAM_ARM7.Mem) + if (NDS.SWRAM_ARM7.Mem) return memregion_SharedWRAM; return memregion_WRAM7; case 0x03800000: @@ -1205,14 +1172,14 @@ int ClassifyAddress7(u32 addr) return memregion_VWRAM; case 0x0C000000: case 0x0C800000: - return (NDS::ConsoleType==1) ? memregion_MainRAM : memregion_Other; + return (NDS.ConsoleType==1) ? memregion_MainRAM : memregion_Other; default: return memregion_Other; } } } -void WifiWrite32(u32 addr, u32 val) +/*void WifiWrite32(u32 addr, u32 val) { Wifi::Write(addr, val & 0xFFFF); Wifi::Write(addr + 2, val >> 16); @@ -1221,18 +1188,18 @@ void WifiWrite32(u32 addr, u32 val) u32 WifiRead32(u32 addr) { return (u32)Wifi::Read(addr) | ((u32)Wifi::Read(addr + 2) << 16); -} +}*/ template void VRAMWrite(u32 addr, T val) { switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: NDS::Current->GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: NDS::Current->GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: NDS::Current->GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: NDS::Current->GPU.WriteVRAM_BOBJ(addr, val); return; + default: NDS::Current->GPU.WriteVRAM_LCDC(addr, val); return; } } template @@ -1240,23 +1207,130 @@ T VRAMRead(u32 addr) { switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return NDS::Current->GPU.ReadVRAM_ABG(addr); + case 0x00200000: return NDS::Current->GPU.ReadVRAM_BBG(addr); + case 0x00400000: return NDS::Current->GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return NDS::Current->GPU.ReadVRAM_BOBJ(addr); + default: return NDS::Current->GPU.ReadVRAM_LCDC(addr); } } -void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) +static u8 GPU3D_Read8(u32 addr) noexcept +{ + return NDS::Current->GPU.GPU3D.Read8(addr); +} + +static u16 GPU3D_Read16(u32 addr) noexcept +{ + return NDS::Current->GPU.GPU3D.Read16(addr); +} + +static u32 GPU3D_Read32(u32 addr) noexcept +{ + return NDS::Current->GPU.GPU3D.Read32(addr); +} + +static void GPU3D_Write8(u32 addr, u8 val) noexcept +{ + NDS::Current->GPU.GPU3D.Write8(addr, val); +} + +static void GPU3D_Write16(u32 addr, u16 val) noexcept +{ + NDS::Current->GPU.GPU3D.Write16(addr, val); +} + +static void GPU3D_Write32(u32 addr, u32 val) noexcept +{ + NDS::Current->GPU.GPU3D.Write32(addr, val); +} + +template +static T GPU_ReadVRAM_ARM7(u32 addr) noexcept +{ + return NDS::Current->GPU.ReadVRAM_ARM7(addr); +} + +template +static void GPU_WriteVRAM_ARM7(u32 addr, T val) noexcept +{ + NDS::Current->GPU.WriteVRAM_ARM7(addr, val); +} + +u32 NDSCartSlot_ReadROMData() +{ // TODO: Add a NDS* parameter, when NDS* is eventually implemented + return NDS::Current->NDSCartSlot.ReadROMData(); +} + +static u8 NDS_ARM9IORead8(u32 addr) +{ + return NDS::Current->ARM9IORead8(addr); +} + +static u16 NDS_ARM9IORead16(u32 addr) +{ + return NDS::Current->ARM9IORead16(addr); +} + +static u32 NDS_ARM9IORead32(u32 addr) +{ + return NDS::Current->ARM9IORead32(addr); +} + +static void NDS_ARM9IOWrite8(u32 addr, u8 val) +{ + NDS::Current->ARM9IOWrite8(addr, val); +} + +static void NDS_ARM9IOWrite16(u32 addr, u16 val) +{ + NDS::Current->ARM9IOWrite16(addr, val); +} + +static void NDS_ARM9IOWrite32(u32 addr, u32 val) +{ + NDS::Current->ARM9IOWrite32(addr, val); +} + +static u8 NDS_ARM7IORead8(u32 addr) +{ + return NDS::Current->ARM7IORead8(addr); +} + +static u16 NDS_ARM7IORead16(u32 addr) +{ + return NDS::Current->ARM7IORead16(addr); +} + +static u32 NDS_ARM7IORead32(u32 addr) +{ + return NDS::Current->ARM7IORead32(addr); +} + +static void NDS_ARM7IOWrite8(u32 addr, u8 val) +{ + NDS::Current->ARM7IOWrite8(addr, val); +} + +static void NDS_ARM7IOWrite16(u32 addr, u16 val) +{ + NDS::Current->ARM7IOWrite16(addr, val); +} + +static void NDS_ARM7IOWrite32(u32 addr, u32 val) +{ + NDS::Current->ARM7IOWrite32(addr, val); +} + +void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept { if (cpu->Num == 0) { switch (addr & 0xFF000000) { case 0x04000000: - if (!store && size == 32 && addr == 0x04100010 && NDS::ExMemCnt[0] & (1<<11)) - return (void*)NDSCart::ReadROMData; + if (!store && size == 32 && addr == 0x04100010 && NDS.ExMemCnt[0] & (1<<11)) + return (void*)NDSCartSlot_ReadROMData; /* unfortunately we can't map GPU2D this way @@ -1268,39 +1342,26 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) { switch (size | store) { - case 8: return (void*)GPU3D::Read8; - case 9: return (void*)GPU3D::Write8; - case 16: return (void*)GPU3D::Read16; - case 17: return (void*)GPU3D::Write16; - case 32: return (void*)GPU3D::Read32; - case 33: return (void*)GPU3D::Write32; + case 8: return (void*)GPU3D_Read8; + case 9: return (void*)GPU3D_Write8; + case 16: return (void*)GPU3D_Read16; + case 17: return (void*)GPU3D_Write16; + case 32: return (void*)GPU3D_Read32; + case 33: return (void*)GPU3D_Write32; } } - if (NDS::ConsoleType == 0) + switch (size | store) { - switch (size | store) - { - case 8: return (void*)NDS::ARM9IORead8; - case 9: return (void*)NDS::ARM9IOWrite8; - case 16: return (void*)NDS::ARM9IORead16; - case 17: return (void*)NDS::ARM9IOWrite16; - case 32: return (void*)NDS::ARM9IORead32; - case 33: return (void*)NDS::ARM9IOWrite32; - } - } - else - { - switch (size | store) - { - case 8: return (void*)DSi::ARM9IORead8; - case 9: return (void*)DSi::ARM9IOWrite8; - case 16: return (void*)DSi::ARM9IORead16; - case 17: return (void*)DSi::ARM9IOWrite16; - case 32: return (void*)DSi::ARM9IORead32; - case 33: return (void*)DSi::ARM9IOWrite32; - } + case 8: return (void*)NDS_ARM9IORead8; + case 9: return (void*)NDS_ARM9IOWrite8; + case 16: return (void*)NDS_ARM9IORead16; + case 17: return (void*)NDS_ARM9IOWrite16; + case 32: return (void*)NDS_ARM9IORead32; + case 33: return (void*)NDS_ARM9IOWrite32; } + // NDS::Current will delegate to the DSi versions of these methods + // if it's really a DSi break; case 0x06000000: switch (size | store) @@ -1320,7 +1381,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) switch (addr & 0xFF800000) { case 0x04000000: - if (addr >= 0x04000400 && addr < 0x04000520) + /*if (addr >= 0x04000400 && addr < 0x04000520) { switch (size | store) { @@ -1331,34 +1392,20 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) case 32: return (void*)SPU::Read32; case 33: return (void*)SPU::Write32; } - } + }*/ - if (NDS::ConsoleType == 0) + switch (size | store) { - switch (size | store) - { - case 8: return (void*)NDS::ARM7IORead8; - case 9: return (void*)NDS::ARM7IOWrite8; - case 16: return (void*)NDS::ARM7IORead16; - case 17: return (void*)NDS::ARM7IOWrite16; - case 32: return (void*)NDS::ARM7IORead32; - case 33: return (void*)NDS::ARM7IOWrite32; - } - } - else - { - switch (size | store) - { - case 8: return (void*)DSi::ARM7IORead8; - case 9: return (void*)DSi::ARM7IOWrite8; - case 16: return (void*)DSi::ARM7IORead16; - case 17: return (void*)DSi::ARM7IOWrite16; - case 32: return (void*)DSi::ARM7IORead32; - case 33: return (void*)DSi::ARM7IOWrite32; - } + case 8: return (void*)NDS_ARM7IORead8; + case 9: return (void*)NDS_ARM7IOWrite8; + case 16: return (void*)NDS_ARM7IORead16; + case 17: return (void*)NDS_ARM7IOWrite16; + case 32: return (void*)NDS_ARM7IORead32; + case 33: return (void*)NDS_ARM7IOWrite32; } break; - case 0x04800000: + // TODO: the wifi funcs also ought to check POWCNT + /*case 0x04800000: if (addr < 0x04810000 && size >= 16) { switch (size | store) @@ -1369,21 +1416,20 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) case 33: return (void*)WifiWrite32; } } - break; + break;*/ case 0x06000000: case 0x06800000: switch (size | store) { - case 8: return (void*)GPU::ReadVRAM_ARM7; - case 9: return (void*)GPU::WriteVRAM_ARM7; - case 16: return (void*)GPU::ReadVRAM_ARM7; - case 17: return (void*)GPU::WriteVRAM_ARM7; - case 32: return (void*)GPU::ReadVRAM_ARM7; - case 33: return (void*)GPU::WriteVRAM_ARM7; + case 8: return (void*)GPU_ReadVRAM_ARM7; + case 9: return (void*)GPU_WriteVRAM_ARM7; + case 16: return (void*)GPU_ReadVRAM_ARM7; + case 17: return (void*)GPU_WriteVRAM_ARM7; + case 32: return (void*)GPU_ReadVRAM_ARM7; + case 33: return (void*)GPU_WriteVRAM_ARM7; } } } return NULL; } - -} +} \ No newline at end of file diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index 726b7108..d36f6032 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,62 +20,209 @@ #define ARMJIT_MEMORY #include "types.h" +#include "MemConstants.h" -#include "ARM.h" +#ifdef JIT_ENABLED +# include "TinyVector.h" +# include "ARM.h" +# if defined(__SWITCH__) +# include +# elif defined(_WIN32) +#include +# else +# include +# include +# include +# include +# include +# endif +#else +# include +#endif -namespace ARMJIT_Memory +namespace melonDS { +#ifdef JIT_ENABLED +namespace Platform { struct DynamicLibrary; } +class Compiler; +class ARMJIT; +#endif -extern void* FastMem9Start; -extern void* FastMem7Start; - -void Init(); -void DeInit(); - -void Reset(); - -enum +constexpr u32 RoundUp(u32 size) noexcept { - memregion_Other = 0, - memregion_ITCM, - memregion_DTCM, - memregion_BIOS9, - memregion_MainRAM, - memregion_SharedWRAM, - memregion_IO9, - memregion_VRAM, - memregion_BIOS7, - memregion_WRAM7, - memregion_IO7, - memregion_Wifi, - memregion_VWRAM, - - // DSi - memregion_BIOS9DSi, - memregion_BIOS7DSi, - memregion_NewSharedWRAM_A, - memregion_NewSharedWRAM_B, - memregion_NewSharedWRAM_C, - - memregions_Count -}; - -int ClassifyAddress9(u32 addr); -int ClassifyAddress7(u32 addr); - -bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize); -u32 LocaliseAddress(int region, u32 num, u32 addr); - -bool IsFastmemCompatible(int region); - -void RemapDTCM(u32 newBase, u32 newSize); -void RemapSWRAM(); -void RemapNWRAM(int num); - -void SetCodeProtection(int region, u32 offset, bool protect); - -void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size); - +#ifdef _WIN32 + return (size + 0xFFFF) & ~0xFFFF; +#else + return size; +#endif } +const u32 MemBlockMainRAMOffset = 0; +const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize); +const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize); +const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize); +const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); +const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize); +const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize); +const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize); + +class ARMJIT_Memory +{ +public: + enum + { + memregion_Other = 0, + memregion_ITCM, + memregion_DTCM, + memregion_BIOS9, + memregion_MainRAM, + memregion_SharedWRAM, + memregion_IO9, + memregion_VRAM, + memregion_BIOS7, + memregion_WRAM7, + memregion_IO7, + memregion_Wifi, + memregion_VWRAM, + + // DSi + memregion_BIOS9DSi, + memregion_BIOS7DSi, + memregion_NewSharedWRAM_A, + memregion_NewSharedWRAM_B, + memregion_NewSharedWRAM_C, + + memregions_Count + }; + +#ifdef JIT_ENABLED +public: + explicit ARMJIT_Memory(melonDS::NDS& nds); + ~ARMJIT_Memory() noexcept; + ARMJIT_Memory(const ARMJIT_Memory&) = delete; + ARMJIT_Memory(ARMJIT_Memory&&) = delete; + ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete; + ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete; + void Reset() noexcept; + void RemapDTCM(u32 newBase, u32 newSize) noexcept; + void RemapSWRAM() noexcept; + void RemapNWRAM(int num) noexcept; + void SetCodeProtection(int region, u32 offset, bool protect) noexcept; + + [[nodiscard]] u8* GetMainRAM() noexcept { return MemoryBase + MemBlockMainRAMOffset; } + [[nodiscard]] const u8* GetMainRAM() const noexcept { return MemoryBase + MemBlockMainRAMOffset; } + + [[nodiscard]] u8* GetSharedWRAM() noexcept { return MemoryBase + MemBlockSWRAMOffset; } + [[nodiscard]] const u8* GetSharedWRAM() const noexcept { return MemoryBase + MemBlockSWRAMOffset; } + + [[nodiscard]] u8* GetARM7WRAM() noexcept { return MemoryBase + MemBlockARM7WRAMOffset; } + [[nodiscard]] const u8* GetARM7WRAM() const noexcept { return MemoryBase + MemBlockARM7WRAMOffset; } + + [[nodiscard]] u8* GetARM9DTCM() noexcept { return MemoryBase + MemBlockDTCMOffset; } + [[nodiscard]] const u8* GetARM9DTCM() const noexcept { return MemoryBase + MemBlockDTCMOffset; } + + [[nodiscard]] u8* GetNWRAM_A() noexcept { return MemoryBase + MemBlockNWRAM_AOffset; } + [[nodiscard]] const u8* GetNWRAM_A() const noexcept { return MemoryBase + MemBlockNWRAM_AOffset; } + + [[nodiscard]] u8* GetNWRAM_B() noexcept { return MemoryBase + MemBlockNWRAM_BOffset; } + [[nodiscard]] const u8* GetNWRAM_B() const noexcept { return MemoryBase + MemBlockNWRAM_BOffset; } + + [[nodiscard]] u8* GetNWRAM_C() noexcept { return MemoryBase + MemBlockNWRAM_COffset; } + [[nodiscard]] const u8* GetNWRAM_C() const noexcept { return MemoryBase + MemBlockNWRAM_COffset; } + + int ClassifyAddress9(u32 addr) const noexcept; + int ClassifyAddress7(u32 addr) const noexcept; + bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept; + u32 LocaliseAddress(int region, u32 num, u32 addr) const noexcept; + bool IsFastmemCompatible(int region) const noexcept; + void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept; + bool MapAtAddress(u32 addr) noexcept; +private: + friend class Compiler; + struct Mapping + { + u32 Addr; + u32 Size, LocalOffset; + u32 Num; + + void Unmap(int region, NDS& nds) noexcept; + }; + + struct FaultDescription + { + u32 EmulatedFaultAddr; + u8* FaultPC; + }; + static bool FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds); + bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; + bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; + void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept; + + melonDS::NDS& NDS; + void* FastMem9Start; + void* FastMem7Start; + u8* MemoryBase = nullptr; +#if defined(__SWITCH__) + VirtmemReservation* FastMem9Reservation, *FastMem7Reservation; + u8* MemoryBaseCodeMem; +#elif defined(_WIN32) + static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo); + HANDLE MemoryFile = INVALID_HANDLE_VALUE; + LPVOID ExceptionHandlerHandle = nullptr; +#else + static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext); + int MemoryFile = -1; +#endif +#ifdef ANDROID + Platform::DynamicLibrary* Libandroid = nullptr; +#endif + u8 MappingStatus9[1 << (32-12)] {}; + u8 MappingStatus7[1 << (32-12)] {}; + TinyVector Mappings[memregions_Count] {}; +#else +public: + explicit ARMJIT_Memory(melonDS::NDS&) {}; + ~ARMJIT_Memory() = default; + ARMJIT_Memory(const ARMJIT_Memory&) = delete; + ARMJIT_Memory(ARMJIT_Memory&&) = delete; + ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete; + ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete; + + void Reset() noexcept {} + void RemapDTCM(u32 newBase, u32 newSize) noexcept {} + void RemapSWRAM() noexcept {} + void RemapNWRAM(int num) noexcept {} + void SetCodeProtection(int region, u32 offset, bool protect) noexcept {} + + [[nodiscard]] u8* GetMainRAM() noexcept { return MainRAM.data(); } + [[nodiscard]] const u8* GetMainRAM() const noexcept { return MainRAM.data(); } + + [[nodiscard]] u8* GetSharedWRAM() noexcept { return SharedWRAM.data(); } + [[nodiscard]] const u8* GetSharedWRAM() const noexcept { return SharedWRAM.data(); } + + [[nodiscard]] u8* GetARM7WRAM() noexcept { return ARM7WRAM.data(); } + [[nodiscard]] const u8* GetARM7WRAM() const noexcept { return ARM7WRAM.data(); } + + [[nodiscard]] u8* GetARM9DTCM() noexcept { return DTCM.data(); } + [[nodiscard]] const u8* GetARM9DTCM() const noexcept { return DTCM.data(); } + + [[nodiscard]] u8* GetNWRAM_A() noexcept { return NWRAM_A.data(); } + [[nodiscard]] const u8* GetNWRAM_A() const noexcept { return NWRAM_A.data(); } + + [[nodiscard]] u8* GetNWRAM_B() noexcept { return NWRAM_B.data(); } + [[nodiscard]] const u8* GetNWRAM_B() const noexcept { return NWRAM_B.data(); } + + [[nodiscard]] u8* GetNWRAM_C() noexcept { return NWRAM_C.data(); } + [[nodiscard]] const u8* GetNWRAM_C() const noexcept { return NWRAM_C.data(); } +private: + std::array MainRAM {}; + std::array ARM7WRAM {}; + std::array SharedWRAM {}; + std::array DTCM {}; + std::array NWRAM_A {}; + std::array NWRAM_B {}; + std::array NWRAM_C {}; +#endif +}; +} #endif diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h index 7ea44ed3..e5f28dd6 100644 --- a/src/ARMJIT_RegisterCache.h +++ b/src/ARMJIT_RegisterCache.h @@ -19,7 +19,6 @@ #ifndef ARMJIT_REGCACHE_H #define ARMJIT_REGCACHE_H -#include "ARMJIT.h" #include "ARMJIT_Internal.h" #include "Platform.h" @@ -28,10 +27,11 @@ #include -namespace ARMJIT +namespace melonDS { using Platform::Log; using Platform::LogLevel; + using namespace Common; // Imported inside the namespace so that other headers aren't polluted template @@ -99,7 +99,7 @@ public: LiteralsLoaded &= ~(1 << reg); } - bool IsLiteral(int reg) + bool IsLiteral(int reg) const { return LiteralsLoaded & (1 << reg); } diff --git a/src/ARMJIT_x64/ARMJIT_ALU.cpp b/src/ARMJIT_x64/ARMJIT_ALU.cpp index ff307873..69449ff9 100644 --- a/src/ARMJIT_x64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_x64/ARMJIT_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -17,10 +17,11 @@ */ #include "ARMJIT_Compiler.h" +#include "../ARM.h" using namespace Gen; -namespace ARMJIT +namespace melonDS { // uses RSCRATCH3 @@ -128,7 +129,7 @@ OpArg Compiler::A_Comp_GetALUOp2(bool S, bool& carryUsed) Comp_AddCycles_C(); u32 shift = (CurInstr.Instr >> 7) & 0x1E; - u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift); + u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift); carryUsed = false; if (S && shift) diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index 281b24a2..f7a01f48 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -17,10 +17,12 @@ */ #include "ARMJIT_Compiler.h" +#include "../ARM.h" +#include "../NDS.h" using namespace Gen; -namespace ARMJIT +namespace melonDS { template @@ -119,7 +121,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1]; + cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1]; CurCPU->R[15] = compileTimePC; } @@ -131,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3]; + cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3]; CurCPU->R[15] = compileTimePC; } diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index ce098710..b18837f3 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -18,7 +18,9 @@ #include "ARMJIT_Compiler.h" +#include "../ARMJIT.h" #include "../ARMInterpreter.h" +#include "../NDS.h" #include #include @@ -33,10 +35,11 @@ #endif using namespace Gen; +using namespace Common; extern "C" void ARM_Ret(); -namespace ARMJIT +namespace melonDS { template <> const X64Reg RegisterCache::NativeRegAllocOrder[] = @@ -140,7 +143,7 @@ void Compiler::A_Comp_MSR() Comp_AddCycles_C(); OpArg val = CurInstr.Instr & (1 << 25) - ? Imm32(::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))) + ? Imm32(melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))) : MapReg(CurInstr.A_Reg(0)); u32 mask = 0; @@ -232,7 +235,7 @@ void Compiler::A_Comp_MSR() */ u8 CodeMemory[1024 * 1024 * 32]; -Compiler::Compiler() +Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) { { #ifdef _WIN32 @@ -648,7 +651,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = { }; #undef F -bool Compiler::CanCompile(bool thumb, u16 kind) +bool Compiler::CanCompile(bool thumb, u16 kind) const { return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL; } @@ -664,7 +667,7 @@ void Compiler::Reset() LoadStorePatches.clear(); } -bool Compiler::IsJITFault(u8* addr) +bool Compiler::IsJITFault(const u8* addr) { return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize; } @@ -712,12 +715,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess... { Log(LogLevel::Debug, "near reset\n"); - ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess... { Log(LogLevel::Debug, "far reset\n"); - ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } ConstantCycles = 0; @@ -861,7 +864,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] void Compiler::Comp_AddCycles_C(bool forceNonConstant) { s32 cycles = Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles); if ((!Thumb && CurInstr.Cond() < 0xE) || forceNonConstant) @@ -873,7 +876,7 @@ void Compiler::Comp_AddCycles_C(bool forceNonConstant) void Compiler::Comp_AddCycles_CI(u32 i) { s32 cycles = (Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + i; if (!Thumb && CurInstr.Cond() < 0xE) @@ -885,7 +888,7 @@ void Compiler::Comp_AddCycles_CI(u32 i) void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add) { s32 cycles = Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles); if (!Thumb && CurInstr.Cond() < 0xE) @@ -910,7 +913,7 @@ void Compiler::Comp_AddCycles_CDI() s32 cycles; - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM @@ -955,7 +958,7 @@ void Compiler::Comp_AddCycles_CD() } else { - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 4) == 0x02) diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index f5817a92..941d8924 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,9 +19,10 @@ #ifndef ARMJIT_X64_COMPILER_H #define ARMJIT_X64_COMPILER_H +#if defined(JIT_ENABLED) && defined(__x86_64__) + #include "../dolphin/x64Emitter.h" -#include "../ARMJIT.h" #include "../ARMJIT_Internal.h" #include "../ARMJIT_RegisterCache.h" @@ -31,9 +32,12 @@ #include -namespace ARMJIT -{ +namespace melonDS +{ +class ARMJIT; +class ARMJIT_Memory; +class NDS; const Gen::X64Reg RCPU = Gen::RBP; const Gen::X64Reg RCPSR = Gen::R15; @@ -79,7 +83,7 @@ struct Op2 class Compiler : public Gen::XEmitter { public: - Compiler(); + explicit Compiler(melonDS::NDS& nds); void Reset(); @@ -88,7 +92,7 @@ public: void LoadReg(int reg, Gen::X64Reg nativeReg); void SaveReg(int reg, Gen::X64Reg nativeReg); - bool CanCompile(bool thumb, u16 kind); + bool CanCompile(bool thumb, u16 kind) const; typedef void (Compiler::*CompileFunc)(); @@ -167,7 +171,7 @@ public: memop_SubtractOffset = 1 << 4 }; void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags); - s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn); + s32 Comp_MemAccessBlock(int rn, Common::BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn); bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr); void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&), @@ -230,7 +234,7 @@ public: SetCodePtr(FarCode); } - bool IsJITFault(u8* addr); + bool IsJITFault(const u8* addr); u8* RewriteMemAccess(u8* pc); @@ -238,44 +242,46 @@ public: void CreateMethod(const char* namefmt, void* start, ...); #endif - u8* FarCode; - u8* NearCode; - u32 FarSize; - u32 NearSize; + melonDS::NDS& NDS; + u8* FarCode {}; + u8* NearCode {}; + u32 FarSize {}; + u32 NearSize {}; - u8* NearStart; - u8* FarStart; + u8* NearStart {}; + u8* FarStart {}; - void* PatchedStoreFuncs[2][2][3][16]; - void* PatchedLoadFuncs[2][2][3][2][16]; + void* PatchedStoreFuncs[2][2][3][16] {}; + void* PatchedLoadFuncs[2][2][3][2][16] {}; - std::unordered_map LoadStorePatches; + std::unordered_map LoadStorePatches {}; - u8* ResetStart; - u32 CodeMemSize; + u8* ResetStart {}; + u32 CodeMemSize {}; - bool Exit; - bool IrregularCycles; + bool Exit {}; + bool IrregularCycles {}; - void* ReadBanked; - void* WriteBanked; + void* ReadBanked {}; + void* WriteBanked {}; bool CPSRDirty = false; - FetchedInstr CurInstr; + FetchedInstr CurInstr {}; - RegisterCache RegCache; + RegisterCache RegCache {}; - bool Thumb; - u32 Num; - u32 R15; - u32 CodeRegion; + bool Thumb {}; + u32 Num {}; + u32 R15 {}; + u32 CodeRegion {}; - u32 ConstantCycles; + u32 ConstantCycles {}; - ARM* CurCPU; + ARM* CurCPU {}; }; } +#endif #endif diff --git a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp index 0961fdd9..e2a74eee 100644 --- a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp +++ b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp @@ -17,7 +17,7 @@ */ #include "../ARM.h" - +using namespace melonDS; int main(int argc, char* argv[]) { FILE* f = fopen("ARMJIT_Offsets.h", "w"); diff --git a/src/ARMJIT_x64/ARMJIT_Linkage.S b/src/ARMJIT_x64/ARMJIT_Linkage.S index fe7b1435..023f6e7b 100644 --- a/src/ARMJIT_x64/ARMJIT_Linkage.S +++ b/src/ARMJIT_x64/ARMJIT_Linkage.S @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -104,3 +104,8 @@ ARM_Ret: #endif ret + +#if !defined(__APPLE__) && !defined(WIN64) +.section .note.GNU-stack,"",@progbits +#endif + diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 99555fff..8520bebc 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -17,10 +17,12 @@ */ #include "ARMJIT_Compiler.h" +#include "../ARMJIT.h" +#include "../NDS.h" using namespace Gen; -namespace ARMJIT +namespace melonDS { template @@ -67,9 +69,9 @@ u8* Compiler::RewriteMemAccess(u8* pc) bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) { - u32 localAddr = LocaliseCodeAddress(Num, addr); + u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr); - int invalidLiteralIdx = InvalidLiterals.Find(localAddr); + int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr); if (invalidLiteralIdx != -1) { return false; @@ -84,7 +86,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) if (size == 32) { CurCPU->DataRead32(addr & ~0x3, &val); - val = ::ROR(val, (addr & 0x3) << 3); + val = melonDS::ROR(val, (addr & 0x3) << 3); } else if (size == 16) { @@ -117,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag if (size == 16) addressMask = ~1; - if (LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -134,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag Comp_AddCycles_CDI(); } - bool addrIsStatic = LiteralOptimizations + bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled() && RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -195,10 +197,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag MOV(32, rnMapped, R(finalAddr)); u32 expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget))) + if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) { if (rdMapped.IsImm()) { @@ -211,12 +213,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16); patch.PatchFunc = flags & memop_Store - ? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()] - : PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()]; + ? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()] + : PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()]; assert(patch.PatchFunc != NULL); - MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start)); + MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start)); X64Reg maskedAddr = RSCRATCH3; if (size > 8) @@ -267,7 +269,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag void* func = NULL; if (addrIsStatic) - func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); + func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); if (func) { @@ -312,7 +314,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag MOV(32, R(ABI_PARAM1), R(RSCRATCH3)); if (flags & memop_Store) { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowWrite9); break; case 16: CALL((void*)&SlowWrite9); break; @@ -324,7 +326,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowRead9); break; case 16: CALL((void*)&SlowRead9); break; @@ -343,7 +345,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag { MOV(32, R(ABI_PARAM2), rdMapped); - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowWrite7); break; case 16: CALL((void*)&SlowWrite7); break; @@ -355,7 +357,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowRead7); break; case 16: CALL((void*)&SlowRead7); break; @@ -421,16 +423,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc s32 offset = (regsCount * 4) * (decrement ? -1 : 1); int expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); if (!store) Comp_AddCycles_CDI(); else Comp_AddCycles_CD(); - bool compileFastPath = FastMemory - && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)); + bool compileFastPath = NDS.JIT.FastMemoryEnabled() + && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)); // we need to make sure that the stack stays aligned to 16 bytes #ifdef _WIN32 @@ -453,7 +455,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc u8* fastPathStart = GetWritableCodePtr(); u8* loadStoreAddr[16]; - MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start)); + MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start)); ADD(64, R(RSCRATCH2), R(RSCRATCH4)); u32 offset = 0; @@ -522,7 +524,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc if (Num == 0) MOV(64, R(ABI_PARAM4), R(RCPU)); - switch (Num * 2 | NDS::ConsoleType) + switch (Num * 2 | NDS.ConsoleType) { case 0: CALL((void*)&SlowBlockTransfer9); break; case 1: CALL((void*)&SlowBlockTransfer9); break; @@ -626,7 +628,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc if (Num == 0) MOV(64, R(ABI_PARAM4), R(RCPU)); - switch (Num * 2 | NDS::ConsoleType) + switch (Num * 2 | NDS.ConsoleType) { case 0: CALL((void*)&SlowBlockTransfer9); break; case 1: CALL((void*)&SlowBlockTransfer9); break; @@ -807,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel() { u32 offset = (CurInstr.Instr & 0xFF) << 2; u32 addr = (R15 & ~0x2) + offset; - if (!LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp index a7171ba0..d53c88f0 100644 --- a/src/ARM_InstrInfo.cpp +++ b/src/ARM_InstrInfo.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,7 +22,7 @@ #include "ARMJIT.h" -namespace ARMInstrInfo +namespace melonDS::ARMInstrInfo { #define ak(x) ((x) << 23) @@ -315,7 +315,7 @@ const u32 T_SVC = T_BranchAlways | T_WriteR14 | tk(tk_SVC); #include "ARM_InstrTable.h" #undef INSTRFUNC_PROTO -Info Decode(bool thumb, u32 num, u32 instr) +Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations) { const u8 FlagsReadPerCond[7] = { flag_Z, @@ -386,7 +386,7 @@ Info Decode(bool thumb, u32 num, u32 instr) { if (res.Kind == tk_LDR_PCREL) { - if (!ARMJIT::LiteralOptimizations) + if (!literaloptimizations) res.SrcRegs |= 1 << 15; res.SpecialKind = special_LoadLiteral; } diff --git a/src/ARM_InstrInfo.h b/src/ARM_InstrInfo.h index 0104027f..13f66b09 100644 --- a/src/ARM_InstrInfo.h +++ b/src/ARM_InstrInfo.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,7 +21,7 @@ #include "types.h" -namespace ARMInstrInfo +namespace melonDS::ARMInstrInfo { // Instruction kinds, for faster dispatch @@ -274,7 +274,7 @@ struct Info } }; -Info Decode(bool thumb, u32 num, u32 instr); +Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations); } diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h index e4b29061..7cdad66d 100644 --- a/src/ARM_InstrTable.h +++ b/src/ARM_InstrTable.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/Args.h b/src/Args.h new file mode 100644 index 00000000..0b20bbf0 --- /dev/null +++ b/src/Args.h @@ -0,0 +1,152 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_ARGS_H +#define MELONDS_ARGS_H + +#include +#include +#include + +#include "NDSCart.h" +#include "GBACart.h" +#include "types.h" +#include "MemConstants.h" +#include "DSi_NAND.h" +#include "FATStorage.h" +#include "FreeBIOS.h" +#include "GPU3D_Soft.h" +#include "SPI_Firmware.h" +#include "SPU.h" + +namespace melonDS +{ +namespace NDSCart { class CartCommon; } +namespace GBACart { class CartCommon; } + +template +constexpr std::array BrokenBIOS = []() constexpr { + std::array broken {}; + + for (int i = 0; i < 16; i++) + { + broken[i*4+0] = 0xE7; + broken[i*4+1] = 0xFF; + broken[i*4+2] = 0xDE; + broken[i*4+3] = 0xFF; + } + + return broken; +}(); + +/// Arguments that configure the JIT. +/// Ignored in builds that don't have the JIT included. +struct JITArgs +{ + unsigned MaxBlockSize = 32; + bool LiteralOptimizations = true; + bool BranchOptimizations = true; + + /// Ignored in builds that have fast memory excluded + /// (even if the JIT is otherwise available). + /// Enabled by default, but frontends should disable this when debugging + /// so the constants segfaults don't hinder debugging. + bool FastMemory = true; +}; + +using ARM9BIOSImage = std::array; +using ARM7BIOSImage = std::array; +using DSiBIOSImage = std::array; + +struct GDBArgs +{ + u16 PortARM7 = 0; + u16 PortARM9 = 0; + bool ARM7BreakOnStartup = false; + bool ARM9BreakOnStartup = false; +}; + +/// Arguments to pass into the NDS constructor. +/// New fields here should have default values if possible. +struct NDSArgs +{ + /// NDS ROM to install. + /// Defaults to nullptr, which means no cart. + /// Should be populated with the desired save data beforehand, + /// including an SD card if applicable. + std::unique_ptr NDSROM = nullptr; + + /// GBA ROM to install. + /// Defaults to nullptr, which means no cart. + /// Should be populated with the desired save data beforehand. + /// Ignored in DSi mode. + std::unique_ptr GBAROM = nullptr; + + /// NDS ARM9 BIOS to install. + /// Defaults to FreeBIOS, which is not compatible with DSi mode. + std::unique_ptr ARM9BIOS = std::make_unique(bios_arm9_bin); + + /// NDS ARM7 BIOS to install. + /// Defaults to FreeBIOS, which is not compatible with DSi mode. + std::unique_ptr ARM7BIOS = std::make_unique(bios_arm7_bin); + + /// Firmware image to install. + /// Defaults to generated NDS firmware. + /// Generated firmware is not compatible with DSi mode. + melonDS::Firmware Firmware {0}; + + /// How the JIT should be configured when initializing. + /// Defaults to enabled, with default settings. + /// To disable the JIT, set this to std::nullopt. + /// Ignored in builds that don't have the JIT included. + std::optional JIT = JITArgs(); + + AudioBitDepth BitDepth = AudioBitDepth::Auto; + AudioInterpolation Interpolation = AudioInterpolation::None; + + /// How the GDB stub should be handled. + /// Defaults to disabled. + /// Ignored in builds that don't have the GDB stub included. + std::optional GDB = std::nullopt; + + /// The 3D renderer to initialize the DS with. + /// Defaults to the software renderer. + /// Can be changed later at any time. + std::unique_ptr Renderer3D = std::make_unique(); +}; + +/// Arguments to pass into the DSi constructor. +/// New fields here should have default values if possible. +/// Contains no virtual methods, so there's no vtable. +struct DSiArgs final : public NDSArgs +{ + std::unique_ptr ARM9iBIOS = std::make_unique(BrokenBIOS); + std::unique_ptr ARM7iBIOS = std::make_unique(BrokenBIOS); + + /// NAND image to install. + /// Required, there is no default value. + DSi_NAND::NANDImage NANDImage; + + /// SD card to install. + /// Defaults to std::nullopt, which means no SD card. + std::optional DSiSDCard; + + bool FullBIOSBoot = false; +}; +} +#endif //MELONDS_ARGS_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 250aef94..50cd7708 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(core STATIC CRC32.cpp DMA.cpp DMA_Timings.h + DMA_Timings.cpp DSi.cpp DSi_AES.cpp DSi_Camera.cpp @@ -25,6 +26,7 @@ add_library(core STATIC DSi_NWifi.cpp DSi_SD.cpp DSi_SPI_TSC.cpp + FATIO.cpp FATStorage.cpp FIFO.h GBACart.cpp @@ -38,6 +40,7 @@ add_library(core STATIC melonDLDI.h NDS.cpp NDSCart.cpp + NDSCartR4.cpp Platform.h ROMList.h ROMList.cpp @@ -49,11 +52,11 @@ add_library(core STATIC SPI_Firmware.cpp SPU.cpp types.h - version.h + Utils.cpp + Utils.h Wifi.cpp WifiAP.cpp - fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c @@ -63,6 +66,15 @@ add_library(core STATIC tiny-AES-c/aes.c xxhash/xxhash.c) +if (ENABLE_GDBSTUB) + message(NOTICE "Enabling GDB stub") + target_sources(core PRIVATE + debug/GdbStub.cpp + debug/GdbProto.cpp + debug/GdbCmds.cpp + ) +endif() + if (ENABLE_OGLRENDERER) target_sources(core PRIVATE GPU_OpenGL.cpp @@ -115,11 +127,26 @@ if (ENABLE_JIT) endif() endif() +set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h") +target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h") +target_include_directories(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") + add_subdirectory(teakra EXCLUDE_FROM_ALL) # Workaround for building teakra with -O0 on Windows either failing or hanging forever target_compile_options(teakra PRIVATE "$<$:-Og>") target_link_libraries(core PRIVATE teakra) +if (NOT MSVC) + # MSVC has its own compiler flag syntax; if we ever support it, + # be sure to silence any equivalent warnings there. + + target_compile_options(core PRIVATE "$<$:-Wno-invalid-offsetof>") + # These warnings are excessive, and are only triggered in the ARMJIT code + # (which is fundamentally non-portable, so this is fine) +endif() + find_library(m MATH_LIBRARY) if (MATH_LIBRARY) @@ -136,15 +163,28 @@ if (ENABLE_JIT) endif() if (WIN32) - target_link_libraries(core PRIVATE ole32 comctl32 ws2_32) -elseif(NOT APPLE) + target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32) +elseif(NOT APPLE AND NOT HAIKU) check_library_exists(rt shm_open "" NEED_LIBRT) if (NEED_LIBRT) target_link_libraries(core PRIVATE rt) endif() +elseif(HAIKU) + target_link_libraries(core PRIVATE network) endif() if (ENABLE_JIT_PROFILING) target_include_directories(core PRIVATE "${VTUNE_INCLUDE_DIR}") target_link_libraries(core PRIVATE "${VTUNE_LIBRARY}") endif() + +#if(CMAKE_BUILD_TYPE MATCHES "Debug") +# set( +# CMAKE_C_FLAGS +# "${CMAKE_C_FLAGS} -fsanitize=undefined -fsanitize=address" +# ) +# target_link_options(core +# BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address +# ) +#endif() + diff --git a/src/CP15.cpp b/src/CP15.cpp index 52fd5604..5e5b35ea 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,12 +22,11 @@ #include "DSi.h" #include "ARM.h" #include "Platform.h" - -#ifdef JIT_ENABLED -#include "ARMJIT.h" #include "ARMJIT_Memory.h" -#endif +#include "ARMJIT.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -125,9 +124,7 @@ void ARMv5::UpdateDTCMSetting() if (newDTCMBase != DTCMBase || newDTCMMask != DTCMMask) { -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapDTCM(newDTCMBase, newDTCMSize); -#endif + NDS.JIT.Memory.RemapDTCM(newDTCMBase, newDTCMSize); DTCMBase = newDTCMBase; DTCMMask = newDTCMMask; } @@ -189,10 +186,14 @@ void ARMv5::UpdatePURegion(u32 n) return; } - u32 start = rgn >> 12; - u32 sz = 2 << ((rgn >> 1) & 0x1F); - u32 end = start + (sz >> 12); - // TODO: check alignment of start + // notes: + // * min size of a pu region is 4KiB (12 bits) + // * size is calculated as size + 1, but the 12 lsb of address space are ignored, therefore we need it as size + 1 - 12, or size - 11 + // * pu regions are aligned based on their size + u32 size = std::max((int)((rgn>>1) & 0x1F) - 11, 0); // obtain the size, subtract 11 and clamp to a min of 0. + u32 start = ((rgn >> 12) >> size) << size; // determine the start offset, and use shifts to force alignment with a multiple of the size. + u32 end = start + (1<> 2]; + u8* bustimings = NDS.ARM9MemTimings[i >> 2]; if (pu & 0x40) { @@ -306,7 +307,7 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend) } else { - MemTimings[i][0] = bustimings[2] << NDS::ARM9ClockShift; + MemTimings[i][0] = bustimings[2] << NDS.ARM9ClockShift; } if (pu & 0x10) @@ -317,9 +318,9 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend) } else { - MemTimings[i][1] = bustimings[0] << NDS::ARM9ClockShift; - MemTimings[i][2] = bustimings[2] << NDS::ARM9ClockShift; - MemTimings[i][3] = bustimings[3] << NDS::ARM9ClockShift; + MemTimings[i][1] = bustimings[0] << NDS.ARM9ClockShift; + MemTimings[i][2] = bustimings[2] << NDS.ARM9ClockShift; + MemTimings[i][3] = bustimings[3] << NDS.ARM9ClockShift; } } } @@ -391,14 +392,14 @@ void ARMv5::ICacheLookup(u32 addr) else { for (int i = 0; i < 32; i+=4) - *(u32*)&ptr[i] = NDS::ARM9Read32(addr+i); + *(u32*)&ptr[i] = NDS.ARM9Read32(addr+i); } ICacheTags[line] = tag; // ouch :/ //printf("cache miss %08X: %d/%d\n", addr, NDS::ARM9MemTimings[addr >> 14][2], NDS::ARM9MemTimings[addr >> 14][3]); - CodeCycles = (NDS::ARM9MemTimings[addr >> 14][2] + (NDS::ARM9MemTimings[addr >> 14][3] * 7)) << NDS::ARM9ClockShift; + CodeCycles = (NDS.ARM9MemTimings[addr >> 14][2] + (NDS.ARM9MemTimings[addr >> 14][3] * 7)) << NDS.ARM9ClockShift; CurICacheLine = ptr; } @@ -582,12 +583,12 @@ void ARMv5::CP15Write(u32 id, u32 val) std::snprintf(log_output, sizeof(log_output), - "PU: region %d = %08X : %s, %08X-%08X\n", + "PU: region %d = %08X : %s, start: %08X size: %02X\n", (id >> 4) & 0xF, val, val & 1 ? "enabled" : "disabled", val & 0xFFFFF000, - (val & 0xFFFFF000) + (2 << ((val & 0x3E) >> 1)) + (val & 0x3E) >> 1 ); Log(LogLevel::Debug, "%s", log_output); // Some implementations of Log imply a newline, so we build up the line before printing it @@ -671,7 +672,7 @@ void ARMv5::CP15Write(u32 id, u32 val) Log(LogLevel::Debug, "unknown CP15 write op %03X %08X\n", id, val); } -u32 ARMv5::CP15Read(u32 id) +u32 ARMv5::CP15Read(u32 id) const { //printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]); @@ -926,9 +927,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val) { DataCycles = 1; *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); -#endif + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -958,9 +957,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val) { DataCycles = 1; *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); -#endif + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -990,9 +987,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val) { DataCycles = 1; *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); -#endif + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -1015,7 +1010,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val) DataCycles += 1; *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; #ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); #endif return; } @@ -1030,7 +1025,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val) DataCycles += MemTimings[addr >> 12][3]; } -void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region) +void ARMv5::GetCodeMemRegion(u32 addr, MemRegion* region) { /*if (addr < ITCMSize) { @@ -1039,6 +1034,7 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region) return; }*/ - GetMemRegion(addr, false, &CodeMem); + NDS.ARM9GetMemRegion(addr, false, &CodeMem); } +} diff --git a/src/CRC32.cpp b/src/CRC32.cpp index 4dec9595..0756c034 100644 --- a/src/CRC32.cpp +++ b/src/CRC32.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,6 +20,8 @@ // http://www.codeproject.com/KB/recipes/crc32_large.aspx +namespace melonDS +{ constexpr u32 _reflect(u32 refl, char ch) { u32 value = 0; @@ -62,3 +64,5 @@ u32 CRC32(const u8 *data, int len, u32 start) return (crc ^ 0xFFFFFFFF); } + +} \ No newline at end of file diff --git a/src/CRC32.h b/src/CRC32.h index 133cf51d..11879057 100644 --- a/src/CRC32.h +++ b/src/CRC32.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -23,6 +23,9 @@ #include "types.h" +namespace melonDS +{ u32 CRC32(const u8* data, int len, u32 start=0); +} #endif // CRC32_H diff --git a/src/DMA.cpp b/src/DMA.cpp index 7d54b413..0fc6cf05 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -25,6 +25,8 @@ #include "DMA_Timings.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -48,21 +50,17 @@ using Platform::LogLevel; // TODO: timings are nonseq when address is fixed/decrementing -DMA::DMA(u32 cpu, u32 num) +DMA::DMA(u32 cpu, u32 num, melonDS::NDS& nds) : + CPU(cpu), + Num(num), + NDS(nds) { - CPU = cpu; - Num = num; - if (cpu == 0) CountMask = 0x001FFFFF; else CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF); } -DMA::~DMA() -{ -} - void DMA::Reset() { SrcAddr = 0; @@ -83,6 +81,7 @@ void DMA::Reset() Executing = false; InProgress = false; MRAMBurstCount = 0; + MRAMBurstTable = DMATiming::MRAMDummy; } void DMA::DoSavestate(Savestate* file) @@ -107,6 +106,10 @@ void DMA::DoSavestate(Savestate* file) file->Bool32(&InProgress); file->Bool32(&IsGXFIFODMA); file->Var32(&MRAMBurstCount); + file->Bool32(&Executing); + file->Bool32(&Stall); + + file->VarArray(MRAMBurstTable.data(), sizeof(MRAMBurstTable)); } void DMA::WriteCnt(u32 val) @@ -143,7 +146,7 @@ void DMA::WriteCnt(u32 val) if ((StartMode & 0x7) == 0) Start(); else if (StartMode == 0x07) - GPU3D::CheckFIFODMA(); + NDS.GPU.GPU3D.CheckFIFODMA(); if (StartMode==0x06 || StartMode==0x13) Log(LogLevel::Warn, "UNIMPLEMENTED ARM%d DMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr); @@ -190,7 +193,7 @@ void DMA::Start() MRAMBurstTable = DMATiming::MRAMDummy; InProgress = true; - NDS::StopCPU(CPU, 1<> 14; u32 dst_id = CurDstAddr >> 14; - u32 src_rgn = NDS::ARM9Regions[src_id]; - u32 dst_rgn = NDS::ARM9Regions[dst_id]; + u32 src_rgn = NDS.ARM9Regions[src_id]; + u32 dst_rgn = NDS.ARM9Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM9MemTimings[src_id][4]; - src_s = NDS::ARM9MemTimings[src_id][5]; - dst_n = NDS::ARM9MemTimings[dst_id][4]; - dst_s = NDS::ARM9MemTimings[dst_id][5]; + src_n = NDS.ARM9MemTimings[src_id][4]; + src_s = NDS.ARM9MemTimings[src_id][5]; + dst_n = NDS.ARM9MemTimings[dst_id][4]; + dst_s = NDS.ARM9MemTimings[dst_id][5]; - if (src_rgn == NDS::Mem9_MainRAM) + if (src_rgn == Mem9_MainRAM) { - if (dst_rgn == NDS::Mem9_MainRAM) + if (dst_rgn == Mem9_MainRAM) return 16; if (SrcAddrInc > 0) @@ -218,7 +221,7 @@ u32 DMA::UnitTimings9_16(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem9_GBAROM) + if (dst_rgn == Mem9_GBAROM) { if (dst_s == 4) MRAMBurstTable = DMATiming::MRAMRead16Bursts[1]; @@ -239,7 +242,7 @@ u32 DMA::UnitTimings9_16(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem9_MainRAM) + else if (dst_rgn == Mem9_MainRAM) { if (DstAddrInc > 0) { @@ -247,7 +250,7 @@ u32 DMA::UnitTimings9_16(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem9_GBAROM) + if (src_rgn == Mem9_GBAROM) { if (src_s == 4) MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1]; @@ -284,18 +287,18 @@ u32 DMA::UnitTimings9_32(bool burststart) u32 src_id = CurSrcAddr >> 14; u32 dst_id = CurDstAddr >> 14; - u32 src_rgn = NDS::ARM9Regions[src_id]; - u32 dst_rgn = NDS::ARM9Regions[dst_id]; + u32 src_rgn = NDS.ARM9Regions[src_id]; + u32 dst_rgn = NDS.ARM9Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM9MemTimings[src_id][6]; - src_s = NDS::ARM9MemTimings[src_id][7]; - dst_n = NDS::ARM9MemTimings[dst_id][6]; - dst_s = NDS::ARM9MemTimings[dst_id][7]; + src_n = NDS.ARM9MemTimings[src_id][6]; + src_s = NDS.ARM9MemTimings[src_id][7]; + dst_n = NDS.ARM9MemTimings[dst_id][6]; + dst_s = NDS.ARM9MemTimings[dst_id][7]; - if (src_rgn == NDS::Mem9_MainRAM) + if (src_rgn == Mem9_MainRAM) { - if (dst_rgn == NDS::Mem9_MainRAM) + if (dst_rgn == Mem9_MainRAM) return 18; if (SrcAddrInc > 0) @@ -304,7 +307,7 @@ u32 DMA::UnitTimings9_32(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem9_GBAROM) + if (dst_rgn == Mem9_GBAROM) { if (dst_s == 8) MRAMBurstTable = DMATiming::MRAMRead32Bursts[2]; @@ -327,7 +330,7 @@ u32 DMA::UnitTimings9_32(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem9_MainRAM) + else if (dst_rgn == Mem9_MainRAM) { if (DstAddrInc > 0) { @@ -335,7 +338,7 @@ u32 DMA::UnitTimings9_32(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem9_GBAROM) + if (src_rgn == Mem9_GBAROM) { if (src_s == 8) MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2]; @@ -376,18 +379,18 @@ u32 DMA::UnitTimings7_16(bool burststart) u32 src_id = CurSrcAddr >> 15; u32 dst_id = CurDstAddr >> 15; - u32 src_rgn = NDS::ARM7Regions[src_id]; - u32 dst_rgn = NDS::ARM7Regions[dst_id]; + u32 src_rgn = NDS.ARM7Regions[src_id]; + u32 dst_rgn = NDS.ARM7Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM7MemTimings[src_id][0]; - src_s = NDS::ARM7MemTimings[src_id][1]; - dst_n = NDS::ARM7MemTimings[dst_id][0]; - dst_s = NDS::ARM7MemTimings[dst_id][1]; + src_n = NDS.ARM7MemTimings[src_id][0]; + src_s = NDS.ARM7MemTimings[src_id][1]; + dst_n = NDS.ARM7MemTimings[dst_id][0]; + dst_s = NDS.ARM7MemTimings[dst_id][1]; - if (src_rgn == NDS::Mem7_MainRAM) + if (src_rgn == Mem7_MainRAM) { - if (dst_rgn == NDS::Mem7_MainRAM) + if (dst_rgn == Mem7_MainRAM) return 16; if (SrcAddrInc > 0) @@ -396,7 +399,7 @@ u32 DMA::UnitTimings7_16(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1) + if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1) { if (dst_s == 4) MRAMBurstTable = DMATiming::MRAMRead16Bursts[1]; @@ -417,7 +420,7 @@ u32 DMA::UnitTimings7_16(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem7_MainRAM) + else if (dst_rgn == Mem7_MainRAM) { if (DstAddrInc > 0) { @@ -425,7 +428,7 @@ u32 DMA::UnitTimings7_16(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1) + if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1) { if (src_s == 4) MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1]; @@ -462,18 +465,18 @@ u32 DMA::UnitTimings7_32(bool burststart) u32 src_id = CurSrcAddr >> 15; u32 dst_id = CurDstAddr >> 15; - u32 src_rgn = NDS::ARM7Regions[src_id]; - u32 dst_rgn = NDS::ARM7Regions[dst_id]; + u32 src_rgn = NDS.ARM7Regions[src_id]; + u32 dst_rgn = NDS.ARM7Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM7MemTimings[src_id][2]; - src_s = NDS::ARM7MemTimings[src_id][3]; - dst_n = NDS::ARM7MemTimings[dst_id][2]; - dst_s = NDS::ARM7MemTimings[dst_id][3]; + src_n = NDS.ARM7MemTimings[src_id][2]; + src_s = NDS.ARM7MemTimings[src_id][3]; + dst_n = NDS.ARM7MemTimings[dst_id][2]; + dst_s = NDS.ARM7MemTimings[dst_id][3]; - if (src_rgn == NDS::Mem7_MainRAM) + if (src_rgn == Mem7_MainRAM) { - if (dst_rgn == NDS::Mem7_MainRAM) + if (dst_rgn == Mem7_MainRAM) return 18; if (SrcAddrInc > 0) @@ -482,7 +485,7 @@ u32 DMA::UnitTimings7_32(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1) + if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1) { if (dst_s == 8) MRAMBurstTable = DMATiming::MRAMRead32Bursts[2]; @@ -505,7 +508,7 @@ u32 DMA::UnitTimings7_32(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem7_MainRAM) + else if (dst_rgn == Mem7_MainRAM) { if (DstAddrInc > 0) { @@ -513,7 +516,7 @@ u32 DMA::UnitTimings7_32(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1) + if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1) { if (src_s == 8) MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2]; @@ -547,10 +550,9 @@ u32 DMA::UnitTimings7_32(bool burststart) } } -template void DMA::Run9() { - if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + if (NDS.ARM9Timestamp >= NDS.ARM9Target) return; Executing = true; @@ -562,40 +564,34 @@ void DMA::Run9() { while (IterCount > 0 && !Stall) { - NDS::ARM9Timestamp += (UnitTimings9_16(burststart) << NDS::ARM9ClockShift); + NDS.ARM9Timestamp += (UnitTimings9_16(burststart) << NDS.ARM9ClockShift); burststart = false; - if (ConsoleType == 1) - DSi::ARM9Write16(CurDstAddr, DSi::ARM9Read16(CurSrcAddr)); - else - NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr)); + NDS.ARM9Write16(CurDstAddr, NDS.ARM9Read16(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<1; CurDstAddr += DstAddrInc<<1; IterCount--; RemCount--; - if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; + if (NDS.ARM9Timestamp >= NDS.ARM9Target) break; } } else { while (IterCount > 0 && !Stall) { - NDS::ARM9Timestamp += (UnitTimings9_32(burststart) << NDS::ARM9ClockShift); + NDS.ARM9Timestamp += (UnitTimings9_32(burststart) << NDS.ARM9ClockShift); burststart = false; - if (ConsoleType == 1) - DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr)); - else - NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr)); + NDS.ARM9Write32(CurDstAddr, NDS.ARM9Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; IterCount--; RemCount--; - if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; + if (NDS.ARM9Timestamp >= NDS.ARM9Target) break; } } @@ -607,10 +603,10 @@ void DMA::Run9() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(0, 1< void DMA::Run7() { - if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + if (NDS.ARM7Timestamp >= NDS.ARM7Target) return; Executing = true; @@ -642,40 +637,34 @@ void DMA::Run7() { while (IterCount > 0 && !Stall) { - NDS::ARM7Timestamp += UnitTimings7_16(burststart); + NDS.ARM7Timestamp += UnitTimings7_16(burststart); burststart = false; - if (ConsoleType == 1) - DSi::ARM7Write16(CurDstAddr, DSi::ARM7Read16(CurSrcAddr)); - else - NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr)); + NDS.ARM7Write16(CurDstAddr, NDS.ARM7Read16(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<1; CurDstAddr += DstAddrInc<<1; IterCount--; RemCount--; - if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; + if (NDS.ARM7Timestamp >= NDS.ARM7Target) break; } } else { while (IterCount > 0 && !Stall) { - NDS::ARM7Timestamp += UnitTimings7_32(burststart); + NDS.ARM7Timestamp += UnitTimings7_32(burststart); burststart = false; - if (ConsoleType == 1) - DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr)); - else - NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr)); + NDS.ARM7Write32(CurDstAddr, NDS.ARM7Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; IterCount--; RemCount--; - if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; + if (NDS.ARM7Timestamp >= NDS.ARM7Target) break; } } @@ -687,7 +676,7 @@ void DMA::Run7() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(1, 1< void DMA::Run() { if (!Running) return; - if (CPU == 0) return Run9(); - else return Run7(); + if (CPU == 0) return Run9(); + else return Run7(); } -template void DMA::Run<0>(); -template void DMA::Run<1>(); +} \ No newline at end of file diff --git a/src/DMA.h b/src/DMA.h index ad194c11..e0e3be15 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,14 +19,19 @@ #ifndef DMA_H #define DMA_H +#include #include "types.h" -#include "Savestate.h" + +namespace melonDS +{ +class NDS; +class Savestate; class DMA { public: - DMA(u32 cpu, u32 num); - ~DMA(); + DMA(u32 cpu, u32 num, NDS& nds); + ~DMA() = default; void Reset(); @@ -40,20 +45,16 @@ public: u32 UnitTimings7_16(bool burststart); u32 UnitTimings7_32(bool burststart); - template void Run(); - - template void Run9(); - template void Run7(); - bool IsInMode(u32 mode) + bool IsInMode(u32 mode) const noexcept { return ((mode == StartMode) && (Cnt & 0x80000000)); } - bool IsRunning() { return Running!=0; } + bool IsRunning() const noexcept { return Running!=0; } void StartIfNeeded(u32 mode) { @@ -72,32 +73,35 @@ public: if (Executing) Stall = true; } - u32 SrcAddr; - u32 DstAddr; - u32 Cnt; + u32 SrcAddr {}; + u32 DstAddr {}; + u32 Cnt {}; private: - u32 CPU, Num; + melonDS::NDS& NDS; + u32 CPU {}; + u32 Num {}; - u32 StartMode; - u32 CurSrcAddr; - u32 CurDstAddr; - u32 RemCount; - u32 IterCount; - s32 SrcAddrInc; - s32 DstAddrInc; - u32 CountMask; + u32 StartMode {}; + u32 CurSrcAddr {}; + u32 CurDstAddr {}; + u32 RemCount {}; + u32 IterCount {}; + s32 SrcAddrInc {}; + s32 DstAddrInc {}; + u32 CountMask {}; - u32 Running; - bool InProgress; + u32 Running {}; + bool InProgress {}; - bool Executing; - bool Stall; + bool Executing {}; + bool Stall {}; - bool IsGXFIFODMA; + bool IsGXFIFODMA {}; - u32 MRAMBurstCount; - const u8* MRAMBurstTable; + u32 MRAMBurstCount {}; + std::array MRAMBurstTable; }; +} #endif diff --git a/src/DMA_Timings.cpp b/src/DMA_Timings.cpp new file mode 100644 index 00000000..912e4e2e --- /dev/null +++ b/src/DMA_Timings.cpp @@ -0,0 +1,243 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "DMA_Timings.h" +#include "types.h" + +namespace melonDS::DMATiming +{ + +// DMA timing tables +// +// DMA timings on the DS are normally straightforward, except in one case: when +// main RAM is involved. +// Main RAM to main RAM is the easy case: 16c/unit in 16bit mode, 18c/unit in 32bit +// mode. +// It gets more complicated when transferring from main RAM to somewhere else, or +// vice versa: main RAM supports burst accesses, but the rules dictating how long +// bursts can be are weird and inconsistent. Main RAM also supports parallel +// memory operations, to some extent. +// I haven't figured out the full logic behind it, let alone how to emulate it +// efficiently, so for now we will use these tables. +// A zero denotes the end of a burst pattern. +// +// Note: burst patterns only apply when the main RAM address is incrementing. +// A fixed or decrementing address results in nonsequential accesses. +// +// Note about GBA slot/wifi timings: these take into account the sequential timing +// setting. Timings are such that the nonseq setting only matters for the first +// access, and minor edge cases (like the last of a 0x20000-byte block). + +extern const std::array MRAMDummy = {0}; + +extern const std::array MRAMRead16Bursts[] = +{ + // main RAM to regular 16bit or 32bit bus (similar) + {7, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 3, + 0}, + // main RAM to GBA/wifi, seq=4 + {8, 6, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, + 0}, + // main RAM to GBA/wifi, seq=6 + {10, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, + 0}, +}; + +extern const std::array MRAMRead32Bursts[] = +{ + // main RAM to regular 16bit bus + {9, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, + 0}, + // main RAM to regular 32bit bus + {9, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 0}, + // main RAM to GBA/wifi, seq=4 + {14, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, + 0}, + // main RAM to GBA/wifi, seq=6 + {18, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, + 0}, +}; + +extern const std::array MRAMWrite16Bursts[] = +{ + // regular 16bit or 32bit bus to main RAM (similar) + {8, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0}, + // GBA/wifi to main RAM, seq=4 + {10, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 0}, + // GBA/wifi to main RAM, seq=6 + {9, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, + 0}, +}; + +extern const std::array MRAMWrite32Bursts[4] = +{ + // regular 16bit bus to main RAM + {9, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0}, + // regular 32bit bus to main RAM + {9, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0}, + // GBA/wifi to main RAM, seq=4 + {15, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, + 0}, + // GBA/wifi to main RAM, seq=6 + {16, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 0}, +}; + +} \ No newline at end of file diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h index 4281c783..63dc4676 100644 --- a/src/DMA_Timings.h +++ b/src/DMA_Timings.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,9 +19,10 @@ #ifndef DMA_TIMINGS_H #define DMA_TIMINGS_H +#include #include "types.h" -namespace DMATiming +namespace melonDS::DMATiming { // DMA timing tables @@ -45,202 +46,15 @@ namespace DMATiming // setting. Timings are such that the nonseq setting only matters for the first // access, and minor edge cases (like the last of a 0x20000-byte block). -constexpr u8 MRAMDummy[1] = {0}; +extern const std::array MRAMDummy; -constexpr u8 MRAMRead16Bursts[][256] = -{ - // main RAM to regular 16bit or 32bit bus (similar) - {7, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, - 7, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, - 7, 3, - 0}, - // main RAM to GBA/wifi, seq=4 - {8, 6, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, - 0}, - // main RAM to GBA/wifi, seq=6 - {10, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, - 0}, -}; +extern const std::array MRAMRead16Bursts[3]; -constexpr u8 MRAMRead32Bursts[][256] = -{ - // main RAM to regular 16bit bus - {9, 4, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, - 0}, - // main RAM to regular 32bit bus - {9, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 0}, - // main RAM to GBA/wifi, seq=4 - {14, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, - 0}, - // main RAM to GBA/wifi, seq=6 - {18, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, - 0}, -}; +extern const std::array MRAMRead32Bursts[4]; -constexpr u8 MRAMWrite16Bursts[][256] = -{ - // regular 16bit or 32bit bus to main RAM (similar) - {8, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0}, - // GBA/wifi to main RAM, seq=4 - {10, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 0}, - // GBA/wifi to main RAM, seq=6 - {9, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, - 0}, -}; +extern const std::array MRAMWrite16Bursts[3]; -constexpr u8 MRAMWrite32Bursts[][256] = -{ - // regular 16bit bus to main RAM - {9, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 0}, - // regular 32bit bus to main RAM - {9, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 0}, - // GBA/wifi to main RAM, seq=4 - {15, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, - 0}, - // GBA/wifi to main RAM, seq=6 - {16, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 0}, -}; +extern const std::array MRAMWrite32Bursts[4]; } diff --git a/src/DSi.cpp b/src/DSi.cpp index 64bc6d2d..306c5d1c 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -17,8 +17,10 @@ */ #include +#include #include #include +#include "Args.h" #include "NDS.h" #include "DSi.h" #include "ARM.h" @@ -28,10 +30,8 @@ #include "DSi_SPI_TSC.h" #include "Platform.h" -#ifdef JIT_ENABLED #include "ARMJIT.h" #include "ARMJIT_Memory.h" -#endif #include "DSi_NDMA.h" #include "DSi_I2C.h" @@ -43,140 +43,95 @@ #include "tiny-AES-c/aes.hpp" +namespace melonDS +{ using namespace Platform; -namespace DSi +// matching NDMA modes for DSi +const u32 NDMAModes[] = { + // ARM9 -u16 SCFG_BIOS; -u16 SCFG_Clock9; -u16 SCFG_Clock7; -u32 SCFG_EXT[2]; -u32 SCFG_MC; -u16 SCFG_RST; + 0x10, // immediate + 0x06, // VBlank + 0x07, // HBlank + 0x08, // scanline start + 0x09, // mainmem FIFO + 0x04, // DS cart slot + 0xFF, // GBA cart slot + 0x0A, // GX FIFO + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -u8 ARM9iBIOS[0x10000] = { 0 }; -u8 ARM7iBIOS[0x10000] = { 0 }; + // ARM7 -u32 MBK[2][9]; + 0x30, // immediate + 0x26, // VBlank + 0x24, // DS cart slot + 0xFF, // wifi / GBA cart slot (TODO) +}; -u8* NWRAM_A; -u8* NWRAM_B; -u8* NWRAM_C; - -u8* NWRAMMap_A[2][4]; -u8* NWRAMMap_B[3][8]; -u8* NWRAMMap_C[3][8]; - -u32 NWRAMStart[2][3]; -u32 NWRAMEnd[2][3]; -u32 NWRAMMask[2][3]; - -u32 NDMACnt[2]; -DSi_NDMA* NDMAs[8]; - -DSi_SDHost* SDMMC; -DSi_SDHost* SDIO; - -u64 ConsoleID; -u8 eMMC_CID[16]; - -// FIXME: these currently have no effect (and aren't stored in a savestate) -// ... not that they matter all that much -u8 GPIO_Data; -u8 GPIO_Dir; -u8 GPIO_IEdgeSel; -u8 GPIO_IE; -u8 GPIO_WiFi; - - -void Set_SCFG_Clock9(u16 val); -void Set_SCFG_MC(u32 val); - - -bool Init() +DSi::DSi(DSiArgs&& args) noexcept : + NDS(std::move(args), 1), + NDMAs { + DSi_NDMA(0, 0, *this), + DSi_NDMA(0, 1, *this), + DSi_NDMA(0, 2, *this), + DSi_NDMA(0, 3, *this), + DSi_NDMA(1, 0, *this), + DSi_NDMA(1, 1, *this), + DSi_NDMA(1, 2, *this), + DSi_NDMA(1, 3, *this), + }, + ARM7iBIOS(*args.ARM7iBIOS), + ARM9iBIOS(*args.ARM9iBIOS), + DSP(*this), + SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)), + SDIO(*this), + I2C(*this), + CamModule(*this), + AES(*this) { -#ifndef JIT_ENABLED - NWRAM_A = new u8[NWRAMSize]; - NWRAM_B = new u8[NWRAMSize]; - NWRAM_C = new u8[NWRAMSize]; -#endif - - if (!DSi_I2C::Init()) return false; - if (!DSi_CamModule::Init()) return false; - if (!DSi_AES::Init()) return false; - if (!DSi_DSP::Init()) return false; - - NDMAs[0] = new DSi_NDMA(0, 0); - NDMAs[1] = new DSi_NDMA(0, 1); - NDMAs[2] = new DSi_NDMA(0, 2); - NDMAs[3] = new DSi_NDMA(0, 3); - NDMAs[4] = new DSi_NDMA(1, 0); - NDMAs[5] = new DSi_NDMA(1, 1); - NDMAs[6] = new DSi_NDMA(1, 2); - NDMAs[7] = new DSi_NDMA(1, 3); - - SDMMC = new DSi_SDHost(0); - SDIO = new DSi_SDHost(1); - - return true; + // Memory is owned by ARMJIT_Memory, don't free it + NWRAM_A = JIT.Memory.GetNWRAM_A(); + NWRAM_B = JIT.Memory.GetNWRAM_B(); + NWRAM_C = JIT.Memory.GetNWRAM_C(); } -void DeInit() +DSi::~DSi() noexcept { -#ifndef JIT_ENABLED - delete[] NWRAM_A; - delete[] NWRAM_B; - delete[] NWRAM_C; - + // Memory is owned externally NWRAM_A = nullptr; NWRAM_B = nullptr; NWRAM_C = nullptr; -#endif - - DSi_I2C::DeInit(); - DSi_CamModule::DeInit(); - DSi_AES::DeInit(); - DSi_DSP::DeInit(); - - for (int i = 0; i < 8; i++) - { - delete NDMAs[i]; - NDMAs[i] = nullptr; - } - - delete SDMMC; - SDMMC = nullptr; - delete SDIO; - SDIO = nullptr; } -void Reset() +void DSi::Reset() { - //NDS::ARM9->CP15Write(0x910, 0x0D00000A); - //NDS::ARM9->CP15Write(0x911, 0x00000020); - //NDS::ARM9->CP15Write(0x100, NDS::ARM9->CP15Read(0x100) | 0x00050000); + //ARM9.CP15Write(0x910, 0x0D00000A); + //ARM9.CP15Write(0x911, 0x00000020); + //ARM9.CP15Write(0x100, ARM9.CP15Read(0x100) | 0x00050000); + NDS::Reset(); - NDS::MapSharedWRAM(3); + // The SOUNDBIAS register does nothing on DSi + SPU.SetApplyBias(false); + KeyInput &= ~(1 << (16+6)); + MapSharedWRAM(3); NDMACnt[0] = 0; NDMACnt[1] = 0; - for (int i = 0; i < 8; i++) NDMAs[i]->Reset(); + for (int i = 0; i < 8; i++) NDMAs[i].Reset(); - DSi_I2C::Reset(); - DSi_CamModule::Reset(); - DSi_DSP::Reset(); - - SDMMC->CloseHandles(); - SDIO->CloseHandles(); + I2C.Reset(); + CamModule.Reset(); + DSP.Reset(); LoadNAND(); - SDMMC->Reset(); - SDIO->Reset(); + SDMMC.Reset(); + SDIO.Reset(); - DSi_AES::Reset(); + AES.Reset(); - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { SCFG_BIOS = 0x0000; } @@ -188,10 +143,10 @@ void Reset() SCFG_Clock7 = 0x0187; SCFG_EXT[0] = 0x8307F100; SCFG_EXT[1] = 0x93FFFB06; - SCFG_MC = 0x0010 | (~((u32)(NDSCart::Cart != nullptr))&1);//0x0011; + SCFG_MC = 0x0010 | (~((u32)(NDSCartSlot.GetCart() != nullptr))&1);//0x0011; SCFG_RST = 0; - DSi_DSP::SetRstLine(false); + DSP.SetRstLine(false); GPIO_Data = 0xff; // these actually initialize to high after reset GPIO_Dir = 0x80; // enable sound out, all others input @@ -200,16 +155,42 @@ void Reset() GPIO_WiFi = 0; // LCD init flag - GPU::DispStat[0] |= (1<<6); - GPU::DispStat[1] |= (1<<6); + GPU.DispStat[0] |= (1<<6); + GPU.DispStat[1] |= (1<<6); } -void Stop() +void DSi::Stop(Platform::StopReason reason) { - DSi_CamModule::Stop(); + NDS::Stop(reason); + CamModule.Stop(); } -void DoSavestate(Savestate* file) +void DSi::SetNDSCart(std::unique_ptr&& cart) +{ + NDS::SetNDSCart(std::move(cart)); + SetCartInserted(NDSCartSlot.GetCart() != nullptr); +} + + +std::unique_ptr DSi::EjectCart() +{ + auto oldcart = NDS::EjectCart(); + + SetCartInserted(false); + + return oldcart; +} + +void DSi::CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) +{ + switch (cam) + { + case 0: return I2C.GetOuterCamera()->InputFrame(data, width, height, rgb); + case 1: return I2C.GetInnerCamera()->InputFrame(data, width, height, rgb); + } +} + +void DSi::DoSavestateExtra(Savestate* file) { file->Section("DSIG"); @@ -233,7 +214,7 @@ void DoSavestate(Savestate* file) { Set_SCFG_Clock9(SCFG_Clock9); Set_SCFG_MC(SCFG_MC); - DSi_DSP::SetRstLine(SCFG_RST & 0x0001); + DSP.SetRstLine(SCFG_RST & 0x0001); MBK[0][8] = 0; MBK[1][8] = 0; @@ -278,17 +259,17 @@ void DoSavestate(Savestate* file) } for (int i = 0; i < 8; i++) - NDMAs[i]->DoSavestate(file); + NDMAs[i].DoSavestate(file); - DSi_AES::DoSavestate(file); - DSi_CamModule::DoSavestate(file); - DSi_DSP::DoSavestate(file); - DSi_I2C::DoSavestate(file); - SDMMC->DoSavestate(file); - SDIO->DoSavestate(file); + AES.DoSavestate(file); + CamModule.DoSavestate(file); + DSP.DoSavestate(file); + I2C.DoSavestate(file); + SDMMC.DoSavestate(file); + SDIO.DoSavestate(file); } -void SetCartInserted(bool inserted) +void DSi::SetCartInserted(bool inserted) { if (inserted) SCFG_MC &= ~1; @@ -296,7 +277,7 @@ void SetCartInserted(bool inserted) SCFG_MC |= 1; } -void DecryptModcryptArea(u32 offset, u32 size, u8* iv) +void DSi::DecryptModcryptArea(u32 offset, u32 size, const u8* iv) { AES_ctx ctx; u8 key[16]; @@ -305,13 +286,13 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv) if ((offset == 0) || (size == 0)) return; - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); if ((header.DSiCryptoFlags & (1<<4)) || (header.AppFlags & (1<<7))) { // dev key - const u8* cartrom = NDSCart::Cart->GetROM(); + const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); memcpy(key, &cartrom[0], 16); } else @@ -395,12 +376,13 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv) } } -void SetupDirectBoot() +void DSi::SetupDirectBoot() { bool dsmode = false; - NDSHeader& header = NDSCart::Cart->GetHeader(); - const u8* cartrom = NDSCart::Cart->GetROM(); - u32 cartid = NDSCart::Cart->ID(); + NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); + const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); + u32 cartid = NDSCartSlot.GetCart()->ID(); + DSi_TSC* tsc = (DSi_TSC*)SPI.GetTSC(); // TODO: add controls for forcing DS or DSi mode? if (!(header.UnitCode & 0x02)) @@ -427,7 +409,7 @@ void SetupDirectBoot() NDS::MapSharedWRAM(3); - DSi_SPI_TSC::SetMode(0x00); + tsc->SetMode(0x00); Set_SCFG_Clock9(0x0000); } else @@ -479,7 +461,7 @@ void SetupDirectBoot() NDS::MapSharedWRAM(mbk[11] >> 24); if (!(header.AppFlags & (1<<0))) - DSi_SPI_TSC::SetMode(0x00); + tsc->SetMode(0x00); } // setup main RAM data @@ -528,32 +510,34 @@ void SetupDirectBoot() ARM9Write32(0x02FFE000+i, tmp); } - if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) - { - u8 userdata[0x1B0]; - DSi_NAND::ReadUserData(userdata); - for (u32 i = 0; i < 0x128; i+=4) - ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]); + if (DSi_NAND::NANDImage* image = SDMMC.GetNAND(); image && *image) + { // If a NAND image is installed, and it's valid... + if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*image)) + { + DSi_NAND::DSiFirmwareSystemSettings userdata {}; + nand.ReadUserData(userdata); + for (u32 i = 0; i < 0x128; i+=4) + ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); - u8 hwinfoS[0xA4]; - u8 hwinfoN[0x9C]; - DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); + DSi_NAND::DSiSerialData hwinfoS {}; + nand.ReadSerialData(hwinfoS); + DSi_NAND::DSiHardwareInfoN hwinfoN; + nand.ReadHardwareInfoN(hwinfoN); - for (u32 i = 0; i < 0x14; i+=4) - ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); + for (u32 i = 0; i < 0x14; i+=4) + ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); - for (u32 i = 0; i < 0x18; i+=4) - ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]); - - DSi_NAND::DeInit(); + for (u32 i = 0; i < 0x18; i+=4) + ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[0x88+i]); + } } - SPI_Firmware::WifiBoard nwifiver = SPI_Firmware::GetFirmware()->Header().WifiBoard; + Firmware::WifiBoard nwifiver = SPI.GetFirmware().GetHeader().WifiBoard; ARM9Write8(0x020005E0, static_cast(nwifiver)); // TODO: these should be taken from the wifi firmware in NAND // but, hey, this works too. - if (nwifiver == SPI_Firmware::WifiBoard::W015) + if (nwifiver == Firmware::WifiBoard::W015) { ARM9Write16(0x020005E2, 0xB57E); ARM9Write32(0x020005E4, 0x00500400); @@ -574,7 +558,7 @@ void SetupDirectBoot() ARM9Write32(0x02FFFC00, cartid); ARM9Write16(0x02FFFC40, 0x0001); // boot indicator - ARM9Write8(0x02FFFDFA, DSi_BPTWL::GetBootFlag() | 0x80); + ARM9Write8(0x02FFFDFA, I2C.GetBPTWL()->GetBootFlag() | 0x80); ARM9Write8(0x02FFFDFB, 0x01); } @@ -586,7 +570,7 @@ void SetupDirectBoot() if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) { u8 securearea[0x800]; - NDSCart::DecryptSecureArea(securearea); + NDSCartSlot.DecryptSecureArea(securearea); for (u32 i = 0; i < 0x800; i+=4) { @@ -636,37 +620,37 @@ void SetupDirectBoot() } } - NDS::ARM7BIOSProt = 0x20; + ARM7BIOSProt = 0x20; - SPI_Firmware::SetupDirectBoot(true); + SPI.GetFirmwareMem()->SetupDirectBoot(); - NDS::ARM9->CP15Write(0x100, 0x00056078); - NDS::ARM9->CP15Write(0x200, 0x0000004A); - NDS::ARM9->CP15Write(0x201, 0x0000004A); - NDS::ARM9->CP15Write(0x300, 0x0000000A); - NDS::ARM9->CP15Write(0x502, 0x15111011); - NDS::ARM9->CP15Write(0x503, 0x05101011); - NDS::ARM9->CP15Write(0x600, 0x04000033); - NDS::ARM9->CP15Write(0x601, 0x04000033); - NDS::ARM9->CP15Write(0x610, 0x02000031); - NDS::ARM9->CP15Write(0x611, 0x02000031); - NDS::ARM9->CP15Write(0x620, 0x00000000); - NDS::ARM9->CP15Write(0x621, 0x00000000); - NDS::ARM9->CP15Write(0x630, 0x08000033); - NDS::ARM9->CP15Write(0x631, 0x08000033); - NDS::ARM9->CP15Write(0x640, 0x0E00001B); - NDS::ARM9->CP15Write(0x641, 0x0E00001B); - NDS::ARM9->CP15Write(0x650, 0x00000000); - NDS::ARM9->CP15Write(0x651, 0x00000000); - NDS::ARM9->CP15Write(0x660, 0xFFFF001D); - NDS::ARM9->CP15Write(0x661, 0xFFFF001D); - NDS::ARM9->CP15Write(0x670, 0x02FFC01B); - NDS::ARM9->CP15Write(0x671, 0x02FFC01B); - NDS::ARM9->CP15Write(0x910, 0x0E00000A); - NDS::ARM9->CP15Write(0x911, 0x00000020); + ARM9.CP15Write(0x100, 0x00056078); + ARM9.CP15Write(0x200, 0x0000004A); + ARM9.CP15Write(0x201, 0x0000004A); + ARM9.CP15Write(0x300, 0x0000000A); + ARM9.CP15Write(0x502, 0x15111011); + ARM9.CP15Write(0x503, 0x05101011); + ARM9.CP15Write(0x600, 0x04000033); + ARM9.CP15Write(0x601, 0x04000033); + ARM9.CP15Write(0x610, 0x02000031); + ARM9.CP15Write(0x611, 0x02000031); + ARM9.CP15Write(0x620, 0x00000000); + ARM9.CP15Write(0x621, 0x00000000); + ARM9.CP15Write(0x630, 0x08000033); + ARM9.CP15Write(0x631, 0x08000033); + ARM9.CP15Write(0x640, 0x0E00001B); + ARM9.CP15Write(0x641, 0x0E00001B); + ARM9.CP15Write(0x650, 0x00000000); + ARM9.CP15Write(0x651, 0x00000000); + ARM9.CP15Write(0x660, 0xFFFF001D); + ARM9.CP15Write(0x661, 0xFFFF001D); + ARM9.CP15Write(0x670, 0x02FFC01B); + ARM9.CP15Write(0x671, 0x02FFC01B); + ARM9.CP15Write(0x910, 0x0E00000A); + ARM9.CP15Write(0x911, 0x00000020); } -void SoftReset() +void DSi::SoftReset() { // TODO: check exactly what is reset // presumably, main RAM isn't reset, since the DSi can be told @@ -676,34 +660,29 @@ void SoftReset() // also, BPTWL[0x70] could be abused to quickly boot specific titles -#ifdef JIT_ENABLED - ARMJIT_Memory::Reset(); - ARMJIT::CheckAndInvalidateITCM(); -#endif + JIT.Reset(); + JIT.CheckAndInvalidateITCM(); - NDS::ARM9->Reset(); - NDS::ARM7->Reset(); + ARM9.Reset(); + ARM7.Reset(); - NDS::ARM9->CP15Reset(); + ARM9.CP15Reset(); NDS::MapSharedWRAM(3); // TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no // *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus // the DSP most likely gets reset - DSi_DSP::Reset(); - - SDMMC->CloseHandles(); - SDIO->CloseHandles(); + DSP.Reset(); LoadNAND(); - SDMMC->Reset(); - SDIO->Reset(); + SDMMC.Reset(); + SDIO.Reset(); - DSi_AES::Reset(); + AES.Reset(); - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { SCFG_BIOS = 0x0000; } @@ -718,25 +697,32 @@ void SoftReset() SCFG_MC = 0x0010;//0x0011; // TODO: is this actually reset? SCFG_RST = 0; - DSi_DSP::SetRstLine(false); + DSP.SetRstLine(false); // LCD init flag - GPU::DispStat[0] |= (1<<6); - GPU::DispStat[1] |= (1<<6); + GPU.DispStat[0] |= (1<<6); + GPU.DispStat[1] |= (1<<6); } -bool LoadNAND() +bool DSi::LoadNAND() { + DSi_NAND::NANDImage* image = SDMMC.GetNAND(); + if (!(image && *image)) + { + Log(LogLevel::Error, "No NAND image loaded\n"); + return false; + } Log(LogLevel::Info, "Loading DSi NAND\n"); - if (!DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) + DSi_NAND::NANDMount nandmount(*SDMMC.GetNAND()); + if (!nandmount) { Log(LogLevel::Error, "Failed to load DSi NAND\n"); return false; } - FileHandle* nand = DSi_NAND::GetFile(); + FileHandle* nand = image->GetFile(); // Make sure NWRAM is accessible. // The Bits are set to the startup values in Reset() and we might @@ -758,7 +744,7 @@ bool LoadNAND() memset(NWRAMMask, 0, sizeof(NWRAMMask)); u32 bootparams[8]; - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { // TODO: figure out default MBK mapping // MBK1..5: disable mappings @@ -892,37 +878,33 @@ bool LoadNAND() } } -#define printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); } -#define printhex_rev(str, size) { for (int z = (size)-1; z >= 0; z--) printf("%02X", (str)[z]); printf("\n"); } + const DSi_NAND::DSiKey& emmccid = image->GetEMMCID(); + Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]); + Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID()); - DSi_NAND::GetIDs(eMMC_CID, ConsoleID); - - Log(LogLevel::Debug, "eMMC CID: "); printhex(eMMC_CID, 16); - Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", ConsoleID); - - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { // point CPUs to boot ROM reset vectors - NDS::ARM9->JumpTo(0xFFFF0000); - NDS::ARM7->JumpTo(0x00000000); + ARM9.JumpTo(0xFFFF0000); + ARM7.JumpTo(0x00000000); } else { u32 eaddr = 0x03FFE6E4; - ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]); - ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]); - ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]); - ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]); + ARM7Write32(eaddr+0x00, *(const u32*)&emmccid[0]); + ARM7Write32(eaddr+0x04, *(const u32*)&emmccid[4]); + ARM7Write32(eaddr+0x08, *(const u32*)&emmccid[8]); + ARM7Write32(eaddr+0x0C, *(const u32*)&emmccid[12]); ARM7Write16(eaddr+0x2C, 0x0001); ARM7Write16(eaddr+0x2E, 0x0001); ARM7Write16(eaddr+0x3C, 0x0100); ARM7Write16(eaddr+0x3E, 0x40E0); ARM7Write16(eaddr+0x42, 0x0001); - memcpy(&NDS::ARM9->ITCM[0x4400], &ARM9iBIOS[0x87F4], 0x400); - memcpy(&NDS::ARM9->ITCM[0x4800], &ARM9iBIOS[0x9920], 0x80); - memcpy(&NDS::ARM9->ITCM[0x4894], &ARM9iBIOS[0x99A0], 0x1048); - memcpy(&NDS::ARM9->ITCM[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048); + memcpy(&ARM9.ITCM[0x4400], &ARM9iBIOS[0x87F4], 0x400); + memcpy(&ARM9.ITCM[0x4800], &ARM9iBIOS[0x9920], 0x80); + memcpy(&ARM9.ITCM[0x4894], &ARM9iBIOS[0x99A0], 0x1048); + memcpy(&ARM9.ITCM[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048); u8 ARM7Init[0x3C00]; memset(ARM7Init, 0, 0x3C00); @@ -935,90 +917,114 @@ bool LoadNAND() ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]); // repoint the CPUs to the boot2 binaries - NDS::ARM9->JumpTo(bootparams[2]); - NDS::ARM7->JumpTo(bootparams[6]); + ARM9.JumpTo(bootparams[2]); + ARM7.JumpTo(bootparams[6]); } - DSi_NAND::PatchUserData(); - - DSi_NAND::DeInit(); + // user data is now expected to be patched by the frontend return true; } -void RunNDMAs(u32 cpu) +void DSi::RunNDMAs(u32 cpu) { // TODO: round-robin mode (requires DMA channels to have a subblock delay set) if (cpu == 0) { - if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + if (ARM9Timestamp >= ARM9Target) return; - if (!(NDS::CPUStop & 0x80000000)) NDMAs[0]->Run(); - if (!(NDS::CPUStop & 0x80000000)) NDMAs[1]->Run(); - if (!(NDS::CPUStop & 0x80000000)) NDMAs[2]->Run(); - if (!(NDS::CPUStop & 0x80000000)) NDMAs[3]->Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[0].Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[1].Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[2].Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[3].Run(); } else { - if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + if (ARM7Timestamp >= ARM7Target) return; - NDMAs[4]->Run(); - NDMAs[5]->Run(); - NDMAs[6]->Run(); - NDMAs[7]->Run(); + NDMAs[4].Run(); + NDMAs[5].Run(); + NDMAs[6].Run(); + NDMAs[7].Run(); } } -void StallNDMAs() +void DSi::StallNDMAs() { // TODO } -bool NDMAsInMode(u32 cpu, u32 mode) + +bool DSi::DMAsInMode(u32 cpu, u32 mode) const +{ + if (NDS::DMAsInMode(cpu, mode)) return true; + + return NDMAsInMode(cpu, NDMAModes[mode]); +} + +bool DSi::DMAsRunning(u32 cpu) const +{ + if (NDS::DMAsRunning(cpu)) return true; + + return NDMAsRunning(cpu); +} + +bool DSi::NDMAsInMode(u32 cpu, u32 mode) const { cpu <<= 2; - if (NDMAs[cpu+0]->IsInMode(mode)) return true; - if (NDMAs[cpu+1]->IsInMode(mode)) return true; - if (NDMAs[cpu+2]->IsInMode(mode)) return true; - if (NDMAs[cpu+3]->IsInMode(mode)) return true; + if (NDMAs[cpu+0].IsInMode(mode)) return true; + if (NDMAs[cpu+1].IsInMode(mode)) return true; + if (NDMAs[cpu+2].IsInMode(mode)) return true; + if (NDMAs[cpu+3].IsInMode(mode)) return true; return false; } -bool NDMAsRunning(u32 cpu) +bool DSi::NDMAsRunning(u32 cpu) const { cpu <<= 2; - if (NDMAs[cpu+0]->IsRunning()) return true; - if (NDMAs[cpu+1]->IsRunning()) return true; - if (NDMAs[cpu+2]->IsRunning()) return true; - if (NDMAs[cpu+3]->IsRunning()) return true; + if (NDMAs[cpu+0].IsRunning()) return true; + if (NDMAs[cpu+1].IsRunning()) return true; + if (NDMAs[cpu+2].IsRunning()) return true; + if (NDMAs[cpu+3].IsRunning()) return true; return false; } -void CheckNDMAs(u32 cpu, u32 mode) +void DSi::CheckNDMAs(u32 cpu, u32 mode) { cpu <<= 2; - NDMAs[cpu+0]->StartIfNeeded(mode); - NDMAs[cpu+1]->StartIfNeeded(mode); - NDMAs[cpu+2]->StartIfNeeded(mode); - NDMAs[cpu+3]->StartIfNeeded(mode); + NDMAs[cpu+0].StartIfNeeded(mode); + NDMAs[cpu+1].StartIfNeeded(mode); + NDMAs[cpu+2].StartIfNeeded(mode); + NDMAs[cpu+3].StartIfNeeded(mode); } -void StopNDMAs(u32 cpu, u32 mode) +void DSi::StopNDMAs(u32 cpu, u32 mode) { cpu <<= 2; - NDMAs[cpu+0]->StopIfNeeded(mode); - NDMAs[cpu+1]->StopIfNeeded(mode); - NDMAs[cpu+2]->StopIfNeeded(mode); - NDMAs[cpu+3]->StopIfNeeded(mode); + NDMAs[cpu+0].StopIfNeeded(mode); + NDMAs[cpu+1].StopIfNeeded(mode); + NDMAs[cpu+2].StopIfNeeded(mode); + NDMAs[cpu+3].StopIfNeeded(mode); } +void DSi::StopDMAs(u32 cpu, u32 mode) +{ + NDS::StopDMAs(cpu, mode); + StopNDMAs(cpu, mode); +} +void DSi::CheckDMAs(u32 cpu, u32 mode) +{ + NDS::CheckDMAs(cpu, mode); + + CheckNDMAs(cpu, NDMAModes[mode]); +} // new WRAM mapping // TODO: find out what happens upon overlapping slots!! -void MapNWRAM_A(u32 num, u8 val) +void DSi::MapNWRAM_A(u32 num, u8 val) { // NWRAM Bank A does not allow all bits to be set // possible non working combinations are caught by later code, but these are not set-able at all @@ -1035,9 +1041,7 @@ void MapNWRAM_A(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(0); -#endif + JIT.Memory.RemapNWRAM(0); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1065,7 +1069,7 @@ void MapNWRAM_A(u32 num, u8 val) } } -void MapNWRAM_B(u32 num, u8 val) +void DSi::MapNWRAM_B(u32 num, u8 val) { // NWRAM Bank B does not allow all bits to be set // possible non working combinations are caught by later code, but these are not set-able at all @@ -1082,9 +1086,7 @@ void MapNWRAM_B(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(1); -#endif + JIT.Memory.RemapNWRAM(1); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1114,7 +1116,7 @@ void MapNWRAM_B(u32 num, u8 val) } } -void MapNWRAM_C(u32 num, u8 val) +void DSi::MapNWRAM_C(u32 num, u8 val) { // NWRAM Bank C does not allow all bits to be set // possible non working combinations are caught by later code, but these are not set-able at all @@ -1131,9 +1133,7 @@ void MapNWRAM_C(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(2); -#endif + JIT.Memory.RemapNWRAM(2); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1163,7 +1163,7 @@ void MapNWRAM_C(u32 num, u8 val) } } -void MapNWRAMRange(u32 cpu, u32 num, u32 val) +void DSi::MapNWRAMRange(u32 cpu, u32 num, u32 val) { // The windowing registers are not writeable in all bits // We need to do this before the change test, so we do not @@ -1182,9 +1182,7 @@ void MapNWRAMRange(u32 cpu, u32 num, u32 val) u32 oldval = MBK[cpu][5+num]; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(num); -#endif + JIT.Memory.RemapNWRAM(num); MBK[cpu][5+num] = val; @@ -1236,41 +1234,41 @@ void MapNWRAMRange(u32 cpu, u32 num, u32 val) } } -void ApplyNewRAMSize(u32 size) +void DSi::ApplyNewRAMSize(u32 size) { switch (size) { case 0: case 1: - NDS::MainRAMMask = 0x3FFFFF; + MainRAMMask = 0x3FFFFF; Log(LogLevel::Debug, "RAM: 4MB\n"); break; case 2: case 3: // TODO: debug console w/ 32MB? - NDS::MainRAMMask = 0xFFFFFF; + MainRAMMask = 0xFFFFFF; Log(LogLevel::Debug, "RAM: 16MB\n"); break; } } -void Set_SCFG_Clock9(u16 val) +void DSi::Set_SCFG_Clock9(u16 val) { - NDS::ARM9Timestamp >>= NDS::ARM9ClockShift; - NDS::ARM9Target >>= NDS::ARM9ClockShift; + ARM9Timestamp >>= ARM9ClockShift; + ARM9Target >>= ARM9ClockShift; Log(LogLevel::Debug, "CLOCK9=%04X\n", val); SCFG_Clock9 = val & 0x0187; - if (SCFG_Clock9 & (1<<0)) NDS::ARM9ClockShift = 2; - else NDS::ARM9ClockShift = 1; + if (SCFG_Clock9 & (1<<0)) ARM9ClockShift = 2; + else ARM9ClockShift = 1; - NDS::ARM9Timestamp <<= NDS::ARM9ClockShift; - NDS::ARM9Target <<= NDS::ARM9ClockShift; - NDS::ARM9->UpdateRegionTimings(0x00000, 0x100000); + ARM9Timestamp <<= ARM9ClockShift; + ARM9Target <<= ARM9ClockShift; + ARM9.UpdateRegionTimings(0x00000, 0x100000); } -void Set_SCFG_MC(u32 val) +void DSi::Set_SCFG_MC(u32 val) { u32 oldslotstatus = SCFG_MC & 0xC; @@ -1281,13 +1279,14 @@ void Set_SCFG_MC(u32 val) if ((oldslotstatus == 0x0) && ((SCFG_MC & 0xC) == 0x4)) { - NDSCart::ResetCart(); + NDSCartSlot.ResetCart(); } } -u8 ARM9Read8(u32 addr) +u8 DSi::ARM9Read8(u32 addr) { + assert(ConsoleType == 1); if ((addr >= 0xFFFF0000) && (!(SCFG_BIOS & (1<<1)))) { if ((addr >= 0xFFFF8000) && (SCFG_BIOS & (1<<0))) @@ -1325,17 +1324,18 @@ u8 ARM9Read8(u32 addr) case 0x08000000: case 0x09000000: case 0x0A000000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFF; + return (ExMemCnt[0] & (1<<7)) ? 0 : 0xFF; case 0x0C000000: - return *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u8*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read8(addr); } -u16 ARM9Read16(u32 addr) +u16 DSi::ARM9Read16(u32 addr) { + assert(ConsoleType == 1); addr &= ~0x1; if ((addr >= 0xFFFF0000) && (!(SCFG_BIOS & (1<<1)))) @@ -1375,17 +1375,18 @@ u16 ARM9Read16(u32 addr) case 0x08000000: case 0x09000000: case 0x0A000000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFF; + return (ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFF; case 0x0C000000: - return *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u16*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read16(addr); } -u32 ARM9Read32(u32 addr) +u32 DSi::ARM9Read32(u32 addr) { + assert(ConsoleType == 1); addr &= ~0x3; if ((addr >= 0xFFFF0000) && (!(SCFG_BIOS & (1<<1)))) @@ -1430,17 +1431,18 @@ u32 ARM9Read32(u32 addr) case 0x08000000: case 0x09000000: case 0x0A000000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFFFFFF; + return (ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFFFFFF; case 0x0C000000: - return *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u32*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read32(addr); } -void ARM9Write8(u32 addr, u8 val) +void DSi::ARM9Write8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr & 0xFF000000) { case 0x03000000: @@ -1460,9 +1462,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u8*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1480,9 +1480,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1500,9 +1498,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1515,16 +1511,14 @@ void ARM9Write8(u32 addr, u8 val) case 0x06000000: if (!(SCFG_EXT[0] & (1<<13))) return; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU.WriteVRAM_BOBJ(addr, val); return; + default: GPU.WriteVRAM_LCDC(addr, val); return; } case 0x08000000: @@ -1533,18 +1527,17 @@ void ARM9Write8(u32 addr, u8 val) return; case 0x0C000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif - *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u8*)&MainRAM[addr & MainRAMMask] = val; return; } return NDS::ARM9Write8(addr, val); } -void ARM9Write16(u32 addr, u16 val) +void DSi::ARM9Write16(u32 addr, u16 val) { + assert(ConsoleType == 1); addr &= ~0x1; switch (addr & 0xFF000000) @@ -1566,9 +1559,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u16*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1586,9 +1577,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1606,9 +1595,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1625,18 +1612,17 @@ void ARM9Write16(u32 addr, u16 val) return; case 0x0C000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif - *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u16*)&MainRAM[addr & MainRAMMask] = val; return; } return NDS::ARM9Write16(addr, val); } -void ARM9Write32(u32 addr, u32 val) +void DSi::ARM9Write32(u32 addr, u32 val) { + assert(ConsoleType == 1); addr &= ~0x3; switch (addr & 0xFF000000) @@ -1658,9 +1644,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u32*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1678,9 +1662,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1698,9 +1680,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1717,24 +1697,23 @@ void ARM9Write32(u32 addr, u32 val) return; case 0x0C000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif - *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u32*)&MainRAM[addr & MainRAMMask] = val; return; } return NDS::ARM9Write32(addr, val); } -bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) +bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) { + assert(ConsoleType == 1); switch (addr & 0xFF000000) { case 0x02000000: case 0x0C000000: - region->Mem = NDS::MainRAM; - region->Mask = NDS::MainRAMMask; + region->Mem = MainRAM; + region->Mask = MainRAMMask; return true; } @@ -1748,12 +1727,12 @@ bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) return false; } - region->Mem = NDS::ARM9BIOS; + region->Mem = &ARM9BIOS[0]; region->Mask = 0xFFF; } else { - region->Mem = ARM9iBIOS; + region->Mem = &ARM9iBIOS[0]; region->Mask = 0xFFFF; } return true; @@ -1765,15 +1744,15 @@ bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) -u8 ARM7Read8(u32 addr) +u8 DSi::ARM7Read8(u32 addr) { if ((addr < 0x00010000) && (!(SCFG_BIOS & (1<<9)))) { if ((addr >= 0x00008000) && (SCFG_BIOS & (1<<8))) return 0xFF; - if (NDS::ARM7->R[15] >= 0x00010000) + if (ARM7.R[15] >= 0x00010000) return 0xFF; - if (addr < NDS::ARM7BIOSProt && NDS::ARM7->R[15] >= NDS::ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFF; return *(u8*)&ARM7iBIOS[addr & 0xFFFF]; @@ -1812,27 +1791,28 @@ u8 ARM7Read8(u32 addr) case 0x09800000: case 0x0A000000: case 0x0A800000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFF : 0; + return (ExMemCnt[0] & (1<<7)) ? 0xFF : 0; case 0x0C000000: case 0x0C800000: - return *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u8*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM7Read8(addr); } -u16 ARM7Read16(u32 addr) +u16 DSi::ARM7Read16(u32 addr) { + assert(ConsoleType == 1); addr &= ~0x1; if ((addr < 0x00010000) && (!(SCFG_BIOS & (1<<9)))) { if ((addr >= 0x00008000) && (SCFG_BIOS & (1<<8))) return 0xFFFF; - if (NDS::ARM7->R[15] >= 0x00010000) + if (ARM7.R[15] >= 0x00010000) return 0xFFFF; - if (addr < NDS::ARM7BIOSProt && NDS::ARM7->R[15] >= NDS::ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFF; return *(u16*)&ARM7iBIOS[addr & 0xFFFF]; @@ -1871,17 +1851,17 @@ u16 ARM7Read16(u32 addr) case 0x09800000: case 0x0A000000: case 0x0A800000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFFFF : 0; + return (ExMemCnt[0] & (1<<7)) ? 0xFFFF : 0; case 0x0C000000: case 0x0C800000: - return *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u16*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM7Read16(addr); } -u32 ARM7Read32(u32 addr) +u32 DSi::ARM7Read32(u32 addr) { addr &= ~0x3; @@ -1889,9 +1869,9 @@ u32 ARM7Read32(u32 addr) { if ((addr >= 0x00008000) && (SCFG_BIOS & (1<<8))) return 0xFFFFFFFF; - if (NDS::ARM7->R[15] >= 0x00010000) + if (ARM7.R[15] >= 0x00010000) return 0xFFFFFFFF; - if (addr < NDS::ARM7BIOSProt && NDS::ARM7->R[15] >= NDS::ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFFFFFF; return *(u32*)&ARM7iBIOS[addr & 0xFFFF]; @@ -1940,8 +1920,9 @@ u32 ARM7Read32(u32 addr) return NDS::ARM7Read32(addr); } -void ARM7Write8(u32 addr, u8 val) +void DSi::ARM7Write8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr & 0xFF800000) { case 0x03000000: @@ -1962,9 +1943,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u8*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1982,9 +1961,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2002,9 +1979,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2025,9 +2000,7 @@ void ARM7Write8(u32 addr, u8 val) case 0x0C000000: case 0x0C800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2035,8 +2008,9 @@ void ARM7Write8(u32 addr, u8 val) return NDS::ARM7Write8(addr, val); } -void ARM7Write16(u32 addr, u16 val) +void DSi::ARM7Write16(u32 addr, u16 val) { + assert(ConsoleType == 1); addr &= ~0x1; switch (addr & 0xFF800000) @@ -2059,9 +2033,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u16*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -2079,9 +2051,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2099,9 +2069,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2122,9 +2090,7 @@ void ARM7Write16(u32 addr, u16 val) case 0x0C000000: case 0x0C800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2132,8 +2098,9 @@ void ARM7Write16(u32 addr, u16 val) return NDS::ARM7Write16(addr, val); } -void ARM7Write32(u32 addr, u32 val) +void DSi::ARM7Write32(u32 addr, u32 val) { + assert(ConsoleType == 1); addr &= ~0x3; switch (addr & 0xFF800000) @@ -2156,9 +2123,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u32*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -2176,9 +2141,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2196,9 +2159,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2219,9 +2180,7 @@ void ARM7Write32(u32 addr, u32 val) case 0x0C000000: case 0x0C800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2229,8 +2188,9 @@ void ARM7Write32(u32 addr, u32 val) return NDS::ARM7Write32(addr, val); } -bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) +bool DSi::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) { + assert(ConsoleType == 1); switch (addr & 0xFF800000) { case 0x02000000: @@ -2274,7 +2234,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) case (addr): return (val) & 0xFFFF; \ case (addr+2): return (val) >> 16; -u8 ARM9IORead8(u32 addr) +u8 DSi::ARM9IORead8(u32 addr) { switch (addr) { @@ -2295,20 +2255,21 @@ u8 ARM9IORead8(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return DSi_CamModule::Read8(addr); + return CamModule.Read8(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSi_DSP::Read8(addr); + return DSP.Read8(addr); } return NDS::ARM9IORead8(addr); } -u16 ARM9IORead16(u32 addr) +u16 DSi::ARM9IORead16(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04004000: return SCFG_BIOS & 0xFF; @@ -2330,20 +2291,21 @@ u16 ARM9IORead16(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return DSi_CamModule::Read16(addr); + return CamModule.Read16(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSi_DSP::Read16(addr); + return DSP.Read16(addr); } return NDS::ARM9IORead16(addr); } -u32 ARM9IORead32(u32 addr) +u32 DSi::ARM9IORead32(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04004000: return SCFG_BIOS & 0xFF; @@ -2362,53 +2324,54 @@ u32 ARM9IORead32(u32 addr) case 0x04004060: return MBK[0][8]; case 0x04004100: return NDMACnt[0]; - case 0x04004104: return NDMAs[0]->SrcAddr; - case 0x04004108: return NDMAs[0]->DstAddr; - case 0x0400410C: return NDMAs[0]->TotalLength; - case 0x04004110: return NDMAs[0]->BlockLength; - case 0x04004114: return NDMAs[0]->SubblockTimer; - case 0x04004118: return NDMAs[0]->FillData; - case 0x0400411C: return NDMAs[0]->Cnt; - case 0x04004120: return NDMAs[1]->SrcAddr; - case 0x04004124: return NDMAs[1]->DstAddr; - case 0x04004128: return NDMAs[1]->TotalLength; - case 0x0400412C: return NDMAs[1]->BlockLength; - case 0x04004130: return NDMAs[1]->SubblockTimer; - case 0x04004134: return NDMAs[1]->FillData; - case 0x04004138: return NDMAs[1]->Cnt; - case 0x0400413C: return NDMAs[2]->SrcAddr; - case 0x04004140: return NDMAs[2]->DstAddr; - case 0x04004144: return NDMAs[2]->TotalLength; - case 0x04004148: return NDMAs[2]->BlockLength; - case 0x0400414C: return NDMAs[2]->SubblockTimer; - case 0x04004150: return NDMAs[2]->FillData; - case 0x04004154: return NDMAs[2]->Cnt; - case 0x04004158: return NDMAs[3]->SrcAddr; - case 0x0400415C: return NDMAs[3]->DstAddr; - case 0x04004160: return NDMAs[3]->TotalLength; - case 0x04004164: return NDMAs[3]->BlockLength; - case 0x04004168: return NDMAs[3]->SubblockTimer; - case 0x0400416C: return NDMAs[3]->FillData; - case 0x04004170: return NDMAs[3]->Cnt; + case 0x04004104: return NDMAs[0].SrcAddr; + case 0x04004108: return NDMAs[0].DstAddr; + case 0x0400410C: return NDMAs[0].TotalLength; + case 0x04004110: return NDMAs[0].BlockLength; + case 0x04004114: return NDMAs[0].SubblockTimer; + case 0x04004118: return NDMAs[0].FillData; + case 0x0400411C: return NDMAs[0].Cnt; + case 0x04004120: return NDMAs[1].SrcAddr; + case 0x04004124: return NDMAs[1].DstAddr; + case 0x04004128: return NDMAs[1].TotalLength; + case 0x0400412C: return NDMAs[1].BlockLength; + case 0x04004130: return NDMAs[1].SubblockTimer; + case 0x04004134: return NDMAs[1].FillData; + case 0x04004138: return NDMAs[1].Cnt; + case 0x0400413C: return NDMAs[2].SrcAddr; + case 0x04004140: return NDMAs[2].DstAddr; + case 0x04004144: return NDMAs[2].TotalLength; + case 0x04004148: return NDMAs[2].BlockLength; + case 0x0400414C: return NDMAs[2].SubblockTimer; + case 0x04004150: return NDMAs[2].FillData; + case 0x04004154: return NDMAs[2].Cnt; + case 0x04004158: return NDMAs[3].SrcAddr; + case 0x0400415C: return NDMAs[3].DstAddr; + case 0x04004160: return NDMAs[3].TotalLength; + case 0x04004164: return NDMAs[3].BlockLength; + case 0x04004168: return NDMAs[3].SubblockTimer; + case 0x0400416C: return NDMAs[3].FillData; + case 0x04004170: return NDMAs[3].Cnt; } if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return DSi_CamModule::Read32(addr); + return CamModule.Read32(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSi_DSP::Read32(addr); + return DSP.Read32(addr); } return NDS::ARM9IORead32(addr); } -void ARM9IOWrite8(u32 addr, u8 val) +void DSi::ARM9IOWrite8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04000301: @@ -2425,7 +2388,7 @@ void ARM9IOWrite8(u32 addr, u8 val) if (!(SCFG_EXT[0] & (1 << 31))) /* no access to SCFG Registers if disabled*/ return; SCFG_RST = (SCFG_RST & 0xFF00) | val; - DSi_DSP::SetRstLine(val & 1); + DSP.SetRstLine(val & 1); return; case 0x04004040: @@ -2465,20 +2428,21 @@ void ARM9IOWrite8(u32 addr, u8 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return DSi_CamModule::Write8(addr, val); + return CamModule.Write8(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSi_DSP::Write8(addr, val); + return DSP.Write8(addr, val); } return NDS::ARM9IOWrite8(addr, val); } -void ARM9IOWrite16(u32 addr, u16 val) +void DSi::ARM9IOWrite16(u32 addr, u16 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04004004: @@ -2491,7 +2455,7 @@ void ARM9IOWrite16(u32 addr, u16 val) if (!(SCFG_EXT[0] & (1 << 31))) /* no access to SCFG Registers if disabled*/ return; SCFG_RST = val; - DSi_DSP::SetRstLine(val & 1); + DSP.SetRstLine(val & 1); return; case 0x04004040: @@ -2525,20 +2489,21 @@ void ARM9IOWrite16(u32 addr, u16 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return DSi_CamModule::Write16(addr, val); + return CamModule.Write16(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSi_DSP::Write16(addr, val); + return DSP.Write16(addr, val); } return NDS::ARM9IOWrite16(addr, val); } -void ARM9IOWrite32(u32 addr, u32 val) +void DSi::ARM9IOWrite32(u32 addr, u32 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04004004: @@ -2546,7 +2511,7 @@ void ARM9IOWrite32(u32 addr, u32 val) return; Set_SCFG_Clock9(val & 0xFFFF); SCFG_RST = val >> 16; - DSi_DSP::SetRstLine((val >> 16) & 1); + DSP.SetRstLine((val >> 16) & 1); break; case 0x04004008: @@ -2581,7 +2546,7 @@ void ARM9IOWrite32(u32 addr, u32 val) // is still busy clearing/relocating shit //if (newram != oldram) // NDS::ScheduleEvent(NDS::Event_DSi_RAMSizeChange, false, 512*512*512, ApplyNewRAMSize, newram); - Log(LogLevel::Debug, "from %08X, ARM7 %08X, %08X\n", NDS::GetPC(0), NDS::GetPC(1), NDS::ARM7->R[1]); + Log(LogLevel::Debug, "from %08X, ARM7 %08X, %08X\n", NDS::GetPC(0), NDS::GetPC(1), ARM7.R[1]); } return; @@ -2642,54 +2607,56 @@ void ARM9IOWrite32(u32 addr, u32 val) return; case 0x04004100: NDMACnt[0] = val & 0x800F0000; return; - case 0x04004104: NDMAs[0]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004108: NDMAs[0]->DstAddr = val & 0xFFFFFFFC; return; - case 0x0400410C: NDMAs[0]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004110: NDMAs[0]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004114: NDMAs[0]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004118: NDMAs[0]->FillData = val; return; - case 0x0400411C: NDMAs[0]->WriteCnt(val); return; - case 0x04004120: NDMAs[1]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004124: NDMAs[1]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004128: NDMAs[1]->TotalLength = val & 0x0FFFFFFF; return; - case 0x0400412C: NDMAs[1]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004130: NDMAs[1]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004134: NDMAs[1]->FillData = val; return; - case 0x04004138: NDMAs[1]->WriteCnt(val); return; - case 0x0400413C: NDMAs[2]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004140: NDMAs[2]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004144: NDMAs[2]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004148: NDMAs[2]->BlockLength = val & 0x00FFFFFF; return; - case 0x0400414C: NDMAs[2]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004150: NDMAs[2]->FillData = val; return; - case 0x04004154: NDMAs[2]->WriteCnt(val); return; - case 0x04004158: NDMAs[3]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x0400415C: NDMAs[3]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004160: NDMAs[3]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004164: NDMAs[3]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004168: NDMAs[3]->SubblockTimer = val & 0x0003FFFF; return; - case 0x0400416C: NDMAs[3]->FillData = val; return; - case 0x04004170: NDMAs[3]->WriteCnt(val); return; + case 0x04004104: NDMAs[0].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004108: NDMAs[0].DstAddr = val & 0xFFFFFFFC; return; + case 0x0400410C: NDMAs[0].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004110: NDMAs[0].BlockLength = val & 0x00FFFFFF; return; + case 0x04004114: NDMAs[0].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004118: NDMAs[0].FillData = val; return; + case 0x0400411C: NDMAs[0].WriteCnt(val); return; + case 0x04004120: NDMAs[1].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004124: NDMAs[1].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004128: NDMAs[1].TotalLength = val & 0x0FFFFFFF; return; + case 0x0400412C: NDMAs[1].BlockLength = val & 0x00FFFFFF; return; + case 0x04004130: NDMAs[1].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004134: NDMAs[1].FillData = val; return; + case 0x04004138: NDMAs[1].WriteCnt(val); return; + case 0x0400413C: NDMAs[2].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004140: NDMAs[2].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004144: NDMAs[2].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004148: NDMAs[2].BlockLength = val & 0x00FFFFFF; return; + case 0x0400414C: NDMAs[2].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004150: NDMAs[2].FillData = val; return; + case 0x04004154: NDMAs[2].WriteCnt(val); return; + case 0x04004158: NDMAs[3].SrcAddr = val & 0xFFFFFFFC; return; + case 0x0400415C: NDMAs[3].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004160: NDMAs[3].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004164: NDMAs[3].BlockLength = val & 0x00FFFFFF; return; + case 0x04004168: NDMAs[3].SubblockTimer = val & 0x0003FFFF; return; + case 0x0400416C: NDMAs[3].FillData = val; return; + case 0x04004170: NDMAs[3].WriteCnt(val); return; } if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return DSi_CamModule::Write32(addr, val); + return CamModule.Write32(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSi_DSP::Write32(addr, val); + return DSP.Write32(addr, val); } return NDS::ARM9IOWrite32(addr, val); } -u8 ARM7IORead8(u32 addr) +u8 DSi::ARM7IORead8(u32 addr) { + assert(ConsoleType == 1); + switch (addr) { case 0x04004000: @@ -2707,21 +2674,21 @@ u8 ARM7IORead8(u32 addr) CASE_READ8_32BIT(0x0400405C, MBK[1][7]) CASE_READ8_32BIT(0x04004060, MBK[1][8]) - case 0x04004500: return DSi_I2C::ReadData(); - case 0x04004501: return DSi_I2C::Cnt; + case 0x04004500: return I2C.ReadData(); + case 0x04004501: return I2C.ReadCnt(); - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFF; - case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 8) & 0xFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 16) & 0xFF; - case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 24) & 0xFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFF; - case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 40) & 0xFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 48) & 0xFF; - case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 56; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFF; + case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 8) & 0xFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFF; + case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 24) & 0xFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFF; + case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 40) & 0xFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 48) & 0xFF; + case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56; case 0x04004D08: return 0; - case 0x4004700: return DSi_DSP::SNDExCnt; - case 0x4004701: return DSi_DSP::SNDExCnt >> 8; + case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF; + case 0x4004701: return DSP.ReadSNDExCnt() >> 8; case 0x04004C00: return GPIO_Data; case 0x04004C01: return GPIO_Dir; @@ -2734,8 +2701,9 @@ u8 ARM7IORead8(u32 addr) return NDS::ARM7IORead8(addr); } -u16 ARM7IORead16(u32 addr) +u16 DSi::ARM7IORead16(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: return NDS::IE2; @@ -2757,13 +2725,13 @@ u16 ARM7IORead16(u32 addr) CASE_READ16_32BIT(0x0400405C, MBK[1][7]) CASE_READ16_32BIT(0x04004060, MBK[1][8]) - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 16) & 0xFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFFFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 48; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFFFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48; case 0x04004D08: return 0; - case 0x4004700: return DSi_DSP::SNDExCnt; + case 0x4004700: return DSP.ReadSNDExCnt(); case 0x04004C00: return GPIO_Data | ((u16)GPIO_Dir << 8); case 0x04004C02: return GPIO_IEdgeSel | ((u16)GPIO_IE << 8); @@ -2772,18 +2740,19 @@ u16 ARM7IORead16(u32 addr) if (addr >= 0x04004800 && addr < 0x04004A00) { - return SDMMC->Read(addr); + return SDMMC.Read(addr); } if (addr >= 0x04004A00 && addr < 0x04004C00) { - return SDIO->Read(addr); + return SDIO.Read(addr); } return NDS::ARM7IORead16(addr); } -u32 ARM7IORead32(u32 addr) +u32 DSi::ARM7IORead32(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: return NDS::IE2; @@ -2804,63 +2773,64 @@ u32 ARM7IORead32(u32 addr) case 0x04004060: return MBK[1][8]; case 0x04004100: return NDMACnt[1]; - case 0x04004104: return NDMAs[4]->SrcAddr; - case 0x04004108: return NDMAs[4]->DstAddr; - case 0x0400410C: return NDMAs[4]->TotalLength; - case 0x04004110: return NDMAs[4]->BlockLength; - case 0x04004114: return NDMAs[4]->SubblockTimer; - case 0x04004118: return NDMAs[4]->FillData; - case 0x0400411C: return NDMAs[4]->Cnt; - case 0x04004120: return NDMAs[5]->SrcAddr; - case 0x04004124: return NDMAs[5]->DstAddr; - case 0x04004128: return NDMAs[5]->TotalLength; - case 0x0400412C: return NDMAs[5]->BlockLength; - case 0x04004130: return NDMAs[5]->SubblockTimer; - case 0x04004134: return NDMAs[5]->FillData; - case 0x04004138: return NDMAs[5]->Cnt; - case 0x0400413C: return NDMAs[6]->SrcAddr; - case 0x04004140: return NDMAs[6]->DstAddr; - case 0x04004144: return NDMAs[6]->TotalLength; - case 0x04004148: return NDMAs[6]->BlockLength; - case 0x0400414C: return NDMAs[6]->SubblockTimer; - case 0x04004150: return NDMAs[6]->FillData; - case 0x04004154: return NDMAs[6]->Cnt; - case 0x04004158: return NDMAs[7]->SrcAddr; - case 0x0400415C: return NDMAs[7]->DstAddr; - case 0x04004160: return NDMAs[7]->TotalLength; - case 0x04004164: return NDMAs[7]->BlockLength; - case 0x04004168: return NDMAs[7]->SubblockTimer; - case 0x0400416C: return NDMAs[7]->FillData; - case 0x04004170: return NDMAs[7]->Cnt; + case 0x04004104: return NDMAs[4].SrcAddr; + case 0x04004108: return NDMAs[4].DstAddr; + case 0x0400410C: return NDMAs[4].TotalLength; + case 0x04004110: return NDMAs[4].BlockLength; + case 0x04004114: return NDMAs[4].SubblockTimer; + case 0x04004118: return NDMAs[4].FillData; + case 0x0400411C: return NDMAs[4].Cnt; + case 0x04004120: return NDMAs[5].SrcAddr; + case 0x04004124: return NDMAs[5].DstAddr; + case 0x04004128: return NDMAs[5].TotalLength; + case 0x0400412C: return NDMAs[5].BlockLength; + case 0x04004130: return NDMAs[5].SubblockTimer; + case 0x04004134: return NDMAs[5].FillData; + case 0x04004138: return NDMAs[5].Cnt; + case 0x0400413C: return NDMAs[6].SrcAddr; + case 0x04004140: return NDMAs[6].DstAddr; + case 0x04004144: return NDMAs[6].TotalLength; + case 0x04004148: return NDMAs[6].BlockLength; + case 0x0400414C: return NDMAs[6].SubblockTimer; + case 0x04004150: return NDMAs[6].FillData; + case 0x04004154: return NDMAs[6].Cnt; + case 0x04004158: return NDMAs[7].SrcAddr; + case 0x0400415C: return NDMAs[7].DstAddr; + case 0x04004160: return NDMAs[7].TotalLength; + case 0x04004164: return NDMAs[7].BlockLength; + case 0x04004168: return NDMAs[7].SubblockTimer; + case 0x0400416C: return NDMAs[7].FillData; + case 0x04004170: return NDMAs[7].Cnt; - case 0x04004400: return DSi_AES::ReadCnt(); - case 0x0400440C: return DSi_AES::ReadOutputFIFO(); + case 0x04004400: return AES.ReadCnt(); + case 0x0400440C: return AES.ReadOutputFIFO(); - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFFFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 32; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFFFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32; case 0x04004D08: return 0; case 0x4004700: - Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", NDS::ARM7->R[15]); - return DSi_DSP::SNDExCnt; + Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", ARM7.R[15]); + return DSP.ReadSNDExCnt(); } if (addr >= 0x04004800 && addr < 0x04004A00) { - if (addr == 0x0400490C) return SDMMC->ReadFIFO32(); - return SDMMC->Read(addr) | (SDMMC->Read(addr+2) << 16); + if (addr == 0x0400490C) return SDMMC.ReadFIFO32(); + return SDMMC.Read(addr) | (SDMMC.Read(addr+2) << 16); } if (addr >= 0x04004A00 && addr < 0x04004C00) { - if (addr == 0x04004B0C) return SDIO->ReadFIFO32(); - return SDIO->Read(addr) | (SDIO->Read(addr+2) << 16); + if (addr == 0x04004B0C) return SDIO.ReadFIFO32(); + return SDIO.Read(addr) | (SDIO.Read(addr+2) << 16); } return NDS::ARM7IORead32(addr); } -void ARM7IOWrite8(u32 addr, u8 val) +void DSi::ARM7IOWrite8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04004000: @@ -2891,14 +2861,14 @@ void ARM7IOWrite8(u32 addr, u8 val) return; } - case 0x04004500: DSi_I2C::WriteData(val); return; - case 0x04004501: DSi_I2C::WriteCnt(val); return; + case 0x04004500: I2C.WriteData(val); return; + case 0x04004501: I2C.WriteCnt(val); return; case 0x4004700: - DSi_DSP::WriteSNDExCnt((u16)val | (DSi_DSP::SNDExCnt & 0xFF00)); + DSP.WriteSNDExCnt((u16)val, 0xFF); return; case 0x4004701: - DSi_DSP::WriteSNDExCnt(((u16)val << 8) | (DSi_DSP::SNDExCnt & 0x00FF)); + DSP.WriteSNDExCnt(((u16)val << 8), 0xFF00); return; case 0x04004C00: @@ -2923,7 +2893,7 @@ void ARM7IOWrite8(u32 addr, u8 val) u32 shift = (addr&3)*8; addr -= 0x04004420; addr &= ~3; - DSi_AES::WriteIV(addr, (u32)val << shift, 0xFF << shift); + AES.WriteIV(addr, (u32)val << shift, 0xFF << shift); return; } if (addr >= 0x04004430 && addr < 0x04004440) @@ -2931,7 +2901,7 @@ void ARM7IOWrite8(u32 addr, u8 val) u32 shift = (addr&3)*8; addr -= 0x04004430; addr &= ~3; - DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFF << shift); + AES.WriteMAC(addr, (u32)val << shift, 0xFF << shift); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -2945,17 +2915,18 @@ void ARM7IOWrite8(u32 addr, u8 val) switch (addr >> 4) { - case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; - case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; - case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 0: AES.WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 1: AES.WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 2: AES.WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; } } return NDS::ARM7IOWrite8(addr, val); } -void ARM7IOWrite16(u32 addr, u16 val) +void DSi::ARM7IOWrite16(u32 addr, u16 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: NDS::IE2 = (val & 0x7FF7); NDS::UpdateIRQ(1); return; @@ -2993,11 +2964,11 @@ void ARM7IOWrite16(u32 addr, u16 val) return; case 0x04004406: - DSi_AES::WriteBlkCnt(val<<16); + AES.WriteBlkCnt(val<<16); return; case 0x4004700: - DSi_DSP::WriteSNDExCnt(val); + DSP.WriteSNDExCnt(val, 0xFFFF); return; case 0x04004C00: @@ -3018,7 +2989,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u32 shift = (addr&1)*16; addr -= 0x04004420; addr &= ~1; - DSi_AES::WriteIV(addr, (u32)val << shift, 0xFFFF << shift); + AES.WriteIV(addr, (u32)val << shift, 0xFFFF << shift); return; } if (addr >= 0x04004430 && addr < 0x04004440) @@ -3026,7 +2997,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u32 shift = (addr&1)*16; addr -= 0x04004430; addr &= ~1; - DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFFFF << shift); + AES.WriteMAC(addr, (u32)val << shift, 0xFFFF << shift); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -3040,28 +3011,29 @@ void ARM7IOWrite16(u32 addr, u16 val) switch (addr >> 4) { - case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; - case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; - case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 0: AES.WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 1: AES.WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 2: AES.WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; } } if (addr >= 0x04004800 && addr < 0x04004A00) { - SDMMC->Write(addr, val); + SDMMC.Write(addr, val); return; } if (addr >= 0x04004A00 && addr < 0x04004C00) { - SDIO->Write(addr, val); + SDIO.Write(addr, val); return; } return NDS::ARM7IOWrite16(addr, val); } -void ARM7IOWrite32(u32 addr, u32 val) +void DSi::ARM7IOWrite32(u32 addr, u32 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: NDS::IE2 = (val & 0x7FF7); NDS::UpdateIRQ(1); return; @@ -3111,55 +3083,55 @@ void ARM7IOWrite32(u32 addr, u32 val) return; case 0x04004100: NDMACnt[1] = val & 0x800F0000; return; - case 0x04004104: NDMAs[4]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004108: NDMAs[4]->DstAddr = val & 0xFFFFFFFC; return; - case 0x0400410C: NDMAs[4]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004110: NDMAs[4]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004114: NDMAs[4]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004118: NDMAs[4]->FillData = val; return; - case 0x0400411C: NDMAs[4]->WriteCnt(val); return; - case 0x04004120: NDMAs[5]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004124: NDMAs[5]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004128: NDMAs[5]->TotalLength = val & 0x0FFFFFFF; return; - case 0x0400412C: NDMAs[5]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004130: NDMAs[5]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004134: NDMAs[5]->FillData = val; return; - case 0x04004138: NDMAs[5]->WriteCnt(val); return; - case 0x0400413C: NDMAs[6]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004140: NDMAs[6]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004144: NDMAs[6]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004148: NDMAs[6]->BlockLength = val & 0x00FFFFFF; return; - case 0x0400414C: NDMAs[6]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004150: NDMAs[6]->FillData = val; return; - case 0x04004154: NDMAs[6]->WriteCnt(val); return; - case 0x04004158: NDMAs[7]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x0400415C: NDMAs[7]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004160: NDMAs[7]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004164: NDMAs[7]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004168: NDMAs[7]->SubblockTimer = val & 0x0003FFFF; return; - case 0x0400416C: NDMAs[7]->FillData = val; return; - case 0x04004170: NDMAs[7]->WriteCnt(val); return; + case 0x04004104: NDMAs[4].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004108: NDMAs[4].DstAddr = val & 0xFFFFFFFC; return; + case 0x0400410C: NDMAs[4].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004110: NDMAs[4].BlockLength = val & 0x00FFFFFF; return; + case 0x04004114: NDMAs[4].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004118: NDMAs[4].FillData = val; return; + case 0x0400411C: NDMAs[4].WriteCnt(val); return; + case 0x04004120: NDMAs[5].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004124: NDMAs[5].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004128: NDMAs[5].TotalLength = val & 0x0FFFFFFF; return; + case 0x0400412C: NDMAs[5].BlockLength = val & 0x00FFFFFF; return; + case 0x04004130: NDMAs[5].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004134: NDMAs[5].FillData = val; return; + case 0x04004138: NDMAs[5].WriteCnt(val); return; + case 0x0400413C: NDMAs[6].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004140: NDMAs[6].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004144: NDMAs[6].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004148: NDMAs[6].BlockLength = val & 0x00FFFFFF; return; + case 0x0400414C: NDMAs[6].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004150: NDMAs[6].FillData = val; return; + case 0x04004154: NDMAs[6].WriteCnt(val); return; + case 0x04004158: NDMAs[7].SrcAddr = val & 0xFFFFFFFC; return; + case 0x0400415C: NDMAs[7].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004160: NDMAs[7].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004164: NDMAs[7].BlockLength = val & 0x00FFFFFF; return; + case 0x04004168: NDMAs[7].SubblockTimer = val & 0x0003FFFF; return; + case 0x0400416C: NDMAs[7].FillData = val; return; + case 0x04004170: NDMAs[7].WriteCnt(val); return; - case 0x04004400: DSi_AES::WriteCnt(val); return; - case 0x04004404: DSi_AES::WriteBlkCnt(val); return; - case 0x04004408: DSi_AES::WriteInputFIFO(val); return; + case 0x04004400: AES.WriteCnt(val); return; + case 0x04004404: AES.WriteBlkCnt(val); return; + case 0x04004408: AES.WriteInputFIFO(val); return; case 0x4004700: - Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, NDS::ARM7->R[15]); - DSi_DSP::WriteSNDExCnt(val); + Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, ARM7.R[15]); + DSP.WriteSNDExCnt(val, 0xFFFF); return; } if (addr >= 0x04004420 && addr < 0x04004430) { addr -= 0x04004420; - DSi_AES::WriteIV(addr, val, 0xFFFFFFFF); + AES.WriteIV(addr, val, 0xFFFFFFFF); return; } if (addr >= 0x04004430 && addr < 0x04004440) { addr -= 0x04004430; - DSi_AES::WriteMAC(addr, val, 0xFFFFFFFF); + AES.WriteMAC(addr, val, 0xFFFFFFFF); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -3170,31 +3142,31 @@ void ARM7IOWrite32(u32 addr, u32 val) switch (addr >> 4) { - case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, val, 0xFFFFFFFF); return; - case 1: DSi_AES::WriteKeyX(n, addr&0xF, val, 0xFFFFFFFF); return; - case 2: DSi_AES::WriteKeyY(n, addr&0xF, val, 0xFFFFFFFF); return; + case 0: AES.WriteKeyNormal(n, addr&0xF, val, 0xFFFFFFFF); return; + case 1: AES.WriteKeyX(n, addr&0xF, val, 0xFFFFFFFF); return; + case 2: AES.WriteKeyY(n, addr&0xF, val, 0xFFFFFFFF); return; } } if (addr >= 0x04004800 && addr < 0x04004A00) { - if (addr == 0x0400490C) { SDMMC->WriteFIFO32(val); return; } - SDMMC->Write(addr, val & 0xFFFF); - SDMMC->Write(addr+2, val >> 16); + if (addr == 0x0400490C) { SDMMC.WriteFIFO32(val); return; } + SDMMC.Write(addr, val & 0xFFFF); + SDMMC.Write(addr+2, val >> 16); return; } if (addr >= 0x04004A00 && addr < 0x04004C00) { - if (addr == 0x04004B0C) { SDIO->WriteFIFO32(val); return; } - SDIO->Write(addr, val & 0xFFFF); - SDIO->Write(addr+2, val >> 16); + if (addr == 0x04004B0C) { SDIO.WriteFIFO32(val); return; } + SDIO.Write(addr, val & 0xFFFF); + SDIO.Write(addr+2, val >> 16); return; } if (addr >= 0x04004300 && addr <= 0x04004400) { - DSi_DSP::Write32(addr, val); + DSP.Write32(addr, val); return; } diff --git a/src/DSi.h b/src/DSi.h index 1ea2cca5..1d010e0f 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,97 +20,167 @@ #define DSI_H #include "NDS.h" +#include "DSi_NDMA.h" #include "DSi_SD.h" +#include "DSi_DSP.h" +#include "DSi_AES.h" +#include "DSi_Camera.h" +#include "DSi_NAND.h" -namespace DSi +namespace melonDS { +class DSi_I2CHost; +class DSi_CamModule; +class DSi_AES; +class DSi_DSP; +class DSiArgs; -extern u16 SCFG_BIOS; -extern u16 SCFG_Clock9; -extern u32 SCFG_EXT[2]; - - -extern u8 ARM9iBIOS[0x10000]; -extern u8 ARM7iBIOS[0x10000]; - -extern u8 eMMC_CID[16]; -extern u64 ConsoleID; - -extern DSi_SDHost* SDMMC; -extern DSi_SDHost* SDIO; - -const u32 NWRAMSize = 0x40000; - -extern u8* NWRAM_A; -extern u8* NWRAM_B; -extern u8* NWRAM_C; - -extern u8* NWRAMMap_A[2][4]; -extern u8* NWRAMMap_B[3][8]; -extern u8* NWRAMMap_C[3][8]; - -extern u32 NWRAMStart[2][3]; -extern u32 NWRAMEnd[2][3]; -extern u32 NWRAMMask[2][3]; - -bool Init(); -void DeInit(); -void Reset(); -void Stop(); - -void DoSavestate(Savestate* file); - -void SetCartInserted(bool inserted); - -void SetupDirectBoot(); -void SoftReset(); - -bool LoadNAND(); - -void RunNDMAs(u32 cpu); -void StallNDMAs(); -bool NDMAsInMode(u32 cpu, u32 mode); -bool NDMAsRunning(u32 cpu); -void CheckNDMAs(u32 cpu, u32 mode); -void StopNDMAs(u32 cpu, u32 mode); - -void MapNWRAM_A(u32 num, u8 val); -void MapNWRAM_B(u32 num, u8 val); -void MapNWRAM_C(u32 num, u8 val); -void MapNWRAMRange(u32 cpu, u32 num, u32 val); - -u8 ARM9Read8(u32 addr); -u16 ARM9Read16(u32 addr); -u32 ARM9Read32(u32 addr); -void ARM9Write8(u32 addr, u8 val); -void ARM9Write16(u32 addr, u16 val); -void ARM9Write32(u32 addr, u32 val); - -bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region); - -u8 ARM7Read8(u32 addr); -u16 ARM7Read16(u32 addr); -u32 ARM7Read32(u32 addr); -void ARM7Write8(u32 addr, u8 val); -void ARM7Write16(u32 addr, u16 val); -void ARM7Write32(u32 addr, u32 val); - -bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region); - -u8 ARM9IORead8(u32 addr); -u16 ARM9IORead16(u32 addr); -u32 ARM9IORead32(u32 addr); -void ARM9IOWrite8(u32 addr, u8 val); -void ARM9IOWrite16(u32 addr, u16 val); -void ARM9IOWrite32(u32 addr, u32 val); - -u8 ARM7IORead8(u32 addr); -u16 ARM7IORead16(u32 addr); -u32 ARM7IORead32(u32 addr); -void ARM7IOWrite8(u32 addr, u8 val); -void ARM7IOWrite16(u32 addr, u16 val); -void ARM7IOWrite32(u32 addr, u32 val); - +namespace DSi_NAND +{ + class NANDImage; } +class DSi final : public NDS +{ +protected: + void DoSavestateExtra(Savestate* file) override; +public: + u16 SCFG_BIOS; + u16 SCFG_Clock9; + u32 SCFG_EXT[2]; + + std::array ARM9iBIOS; + std::array ARM7iBIOS; + DSi_SDHost SDMMC; + DSi_SDHost SDIO; + + const u32 NWRAMSize = 0x40000; + + u8* NWRAM_A; + u8* NWRAM_B; + u8* NWRAM_C; + + u8* NWRAMMap_A[2][4]; + u8* NWRAMMap_B[3][8]; + u8* NWRAMMap_C[3][8]; + + u32 NWRAMStart[2][3]; + u32 NWRAMEnd[2][3]; + u32 NWRAMMask[2][3]; + + DSi_I2CHost I2C; + DSi_CamModule CamModule; + DSi_AES AES; + DSi_DSP DSP; + + void Reset() override; + void Stop(Platform::StopReason reason) override; + + bool DoSavestate(Savestate* file); + + void SetCartInserted(bool inserted); + + void SetupDirectBoot() override; + void SoftReset(); + + bool LoadNAND(); + + void RunNDMAs(u32 cpu); + void StallNDMAs(); + bool NDMAsInMode(u32 cpu, u32 mode) const; + bool NDMAsRunning(u32 cpu) const; + void CheckNDMAs(u32 cpu, u32 mode); + void StopNDMAs(u32 cpu, u32 mode); + + void MapNWRAM_A(u32 num, u8 val); + void MapNWRAM_B(u32 num, u8 val); + void MapNWRAM_C(u32 num, u8 val); + void MapNWRAMRange(u32 cpu, u32 num, u32 val); + + u8 ARM9Read8(u32 addr) override; + u16 ARM9Read16(u32 addr) override; + u32 ARM9Read32(u32 addr) override; + void ARM9Write8(u32 addr, u8 val) override; + void ARM9Write16(u32 addr, u16 val) override; + void ARM9Write32(u32 addr, u32 val) override; + + bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) override; + + u8 ARM7Read8(u32 addr) override; + u16 ARM7Read16(u32 addr) override; + u32 ARM7Read32(u32 addr) override; + void ARM7Write8(u32 addr, u8 val) override; + void ARM7Write16(u32 addr, u16 val) override; + void ARM7Write32(u32 addr, u32 val) override; + + bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) override; + + u8 ARM9IORead8(u32 addr) override; + u16 ARM9IORead16(u32 addr) override; + u32 ARM9IORead32(u32 addr) override; + void ARM9IOWrite8(u32 addr, u8 val) override; + void ARM9IOWrite16(u32 addr, u16 val) override; + void ARM9IOWrite32(u32 addr, u32 val) override; + + u8 ARM7IORead8(u32 addr) override; + u16 ARM7IORead16(u32 addr) override; + u32 ARM7IORead32(u32 addr) override; + void ARM7IOWrite8(u32 addr, u8 val) override; + void ARM7IOWrite16(u32 addr, u16 val) override; + void ARM7IOWrite32(u32 addr, u32 val) override; + +public: + DSi(DSiArgs&& args) noexcept; + ~DSi() noexcept override; + DSi(const DSi&) = delete; + DSi& operator=(const DSi&) = delete; + DSi(DSi&&) = delete; + DSi& operator=(DSi&&) = delete; + void SetNDSCart(std::unique_ptr&& cart) override; + std::unique_ptr EjectCart() override; + bool NeedsDirectBoot() const override + { + // for now, DSi mode requires original BIOS/NAND + return false; + } + + [[nodiscard]] const DSi_NAND::NANDImage& GetNAND() const noexcept { return *SDMMC.GetNAND(); } + [[nodiscard]] DSi_NAND::NANDImage& GetNAND() noexcept { return *SDMMC.GetNAND(); } + void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { SDMMC.SetNAND(std::move(nand)); } + u64 GetConsoleID() const noexcept { return SDMMC.GetNAND()->GetConsoleID(); } + + [[nodiscard]] const FATStorage* GetSDCard() const noexcept { return SDMMC.GetSDCard(); } + void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); } + void SetSDCard(std::optional&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); } + + void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) override; + bool DMAsInMode(u32 cpu, u32 mode) const override; + bool DMAsRunning(u32 cpu) const override; + void StopDMAs(u32 cpu, u32 mode) override; + void CheckDMAs(u32 cpu, u32 mode) override; + u16 SCFG_Clock7; + u32 SCFG_MC; + u16 SCFG_RST; + u32 MBK[2][9]; + u32 NDMACnt[2]; + std::array NDMAs; + // FIXME: these currently have no effect (and aren't stored in a savestate) + // ... not that they matter all that much + u8 GPIO_Data; + u8 GPIO_Dir; + u8 GPIO_IEdgeSel; + u8 GPIO_IE; + u8 GPIO_WiFi; + + bool GetFullBIOSBoot() const noexcept { return FullBIOSBoot; } + void SetFullBIOSBoot(bool full) noexcept { FullBIOSBoot = full; } +private: + bool FullBIOSBoot; + void Set_SCFG_Clock9(u16 val); + void Set_SCFG_MC(u32 val); + void DecryptModcryptArea(u32 offset, u32 size, const u8* iv); + void ApplyNewRAMSize(u32 size); +}; + +} #endif // DSI_H diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 5b1fc539..379dea13 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,64 +19,15 @@ #include #include #include "DSi.h" +#include "DSi_NAND.h" #include "DSi_AES.h" -#include "FIFO.h" -#include "tiny-AES-c/aes.hpp" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace DSi_AES -{ - -u32 Cnt; - -u32 BlkCnt; -u32 RemExtra; -u32 RemBlocks; - -bool OutputFlush; - -u32 InputDMASize, OutputDMASize; -u32 AESMode; - -FIFO InputFIFO; -FIFO OutputFIFO; - -u8 IV[16]; - -u8 MAC[16]; - -u8 KeyNormal[4][16]; -u8 KeyX[4][16]; -u8 KeyY[4][16]; - -u8 CurKey[16]; -u8 CurMAC[16]; - -// output MAC for CCM encrypt -u8 OutputMAC[16]; -bool OutputMACDue; - -AES_ctx Ctx; - -void ROL16(u8* val, u32 n) -{ - u32 n_coarse = n >> 3; - u32 n_fine = n & 7; - u8 tmp[16]; - - for (u32 i = 0; i < 16; i++) - { - tmp[i] = val[(i - n_coarse) & 0xF]; - } - - for (u32 i = 0; i < 16; i++) - { - val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine)); - } -} #define _printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); } #define _printhex2(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); } @@ -85,19 +36,17 @@ void ROL16(u8* val, u32 n) #define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); } -bool Init() +DSi_AES::DSi_AES(melonDS::DSi& dsi) : DSi(dsi) { const u8 zero[16] = {0}; AES_init_ctx_iv(&Ctx, zero, zero); - - return true; } -void DeInit() +DSi_AES::~DSi_AES() { } -void Reset() +void DSi_AES::Reset() { Cnt = 0; @@ -129,6 +78,7 @@ void Reset() OutputMACDue = false; // initialize keys + u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID(); // slot 0: modcrypt *(u32*)&KeyX[0][0] = 0x746E694E; @@ -137,20 +87,20 @@ void Reset() // slot 1: 'Tad'/dev.kp *(u32*)&KeyX[1][0] = 0x4E00004A; *(u32*)&KeyX[1][4] = 0x4A00004E; - *(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72; - *(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID; + *(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72; + *(u32*)&KeyX[1][12] = (u32)consoleid; // slot 3: console-unique eMMC crypto - *(u32*)&KeyX[3][0] = (u32)DSi::ConsoleID; - *(u32*)&KeyX[3][4] = (u32)DSi::ConsoleID ^ 0x24EE6906; - *(u32*)&KeyX[3][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xE65B601D; - *(u32*)&KeyX[3][12] = (u32)(DSi::ConsoleID >> 32); + *(u32*)&KeyX[3][0] = (u32)consoleid; + *(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906; + *(u32*)&KeyX[3][8] = (u32)(consoleid >> 32) ^ 0xE65B601D; + *(u32*)&KeyX[3][12] = (u32)(consoleid >> 32); *(u32*)&KeyY[3][0] = 0x0AB9DC76; *(u32*)&KeyY[3][4] = 0xBD4DC4D3; *(u32*)&KeyY[3][8] = 0x202DDD1D; } -void DoSavestate(Savestate* file) +void DSi_AES::DoSavestate(Savestate* file) { file->Section("AESi"); @@ -188,7 +138,7 @@ void DoSavestate(Savestate* file) } -void ProcessBlock_CCM_Extra() +void DSi_AES::ProcessBlock_CCM_Extra() { u8 data[16]; u8 data_rev[16]; @@ -204,7 +154,7 @@ void ProcessBlock_CCM_Extra() AES_ECB_encrypt(&Ctx, CurMAC); } -void ProcessBlock_CCM_Decrypt() +void DSi_AES::ProcessBlock_CCM_Decrypt() { u8 data[16]; u8 data_rev[16]; @@ -232,7 +182,7 @@ void ProcessBlock_CCM_Decrypt() OutputFIFO.Write(*(u32*)&data[12]); } -void ProcessBlock_CCM_Encrypt() +void DSi_AES::ProcessBlock_CCM_Encrypt() { u8 data[16]; u8 data_rev[16]; @@ -260,7 +210,7 @@ void ProcessBlock_CCM_Encrypt() OutputFIFO.Write(*(u32*)&data[12]); } -void ProcessBlock_CTR() +void DSi_AES::ProcessBlock_CTR() { u8 data[16]; u8 data_rev[16]; @@ -285,7 +235,7 @@ void ProcessBlock_CTR() } -u32 ReadCnt() +u32 DSi_AES::ReadCnt() const { u32 ret = Cnt; @@ -295,7 +245,7 @@ u32 ReadCnt() return ret; } -void WriteCnt(u32 val) +void DSi_AES::WriteCnt(u32 val) { u32 oldcnt = Cnt; Cnt = val & 0xFC1FF000; @@ -363,7 +313,7 @@ void WriteCnt(u32 val) AES_init_ctx_iv(&Ctx, key, iv); } - DSi::CheckNDMAs(1, 0x2A); + DSi.CheckNDMAs(1, 0x2A); } else { @@ -378,12 +328,12 @@ void WriteCnt(u32 val) // val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks, BlkCnt); } -void WriteBlkCnt(u32 val) +void DSi_AES::WriteBlkCnt(u32 val) { BlkCnt = val; } -u32 ReadOutputFIFO() +u32 DSi_AES::ReadOutputFIFO() { if (OutputFIFO.IsEmpty()) Log(LogLevel::Warn, "!!! AES OUTPUT FIFO EMPTY\n"); @@ -397,9 +347,9 @@ u32 ReadOutputFIFO() else { if (OutputFIFO.Level() > 0) - DSi::CheckNDMAs(1, 0x2B); + DSi.CheckNDMAs(1, 0x2B); else - DSi::StopNDMAs(1, 0x2B); + DSi.StopNDMAs(1, 0x2B); if (OutputMACDue && OutputFIFO.Level() <= 12) { @@ -414,7 +364,7 @@ u32 ReadOutputFIFO() return ret; } -void WriteInputFIFO(u32 val) +void DSi_AES::WriteInputFIFO(u32 val) { // TODO: add some delay to processing @@ -427,29 +377,29 @@ void WriteInputFIFO(u32 val) Update(); } -void CheckInputDMA() +void DSi_AES::CheckInputDMA() { if (RemBlocks == 0 && RemExtra == 0) return; if (InputFIFO.Level() <= InputDMASize) { // trigger input DMA - DSi::CheckNDMAs(1, 0x2A); + DSi.CheckNDMAs(1, 0x2A); } Update(); } -void CheckOutputDMA() +void DSi_AES::CheckOutputDMA() { if (OutputFIFO.Level() >= OutputDMASize) { // trigger output DMA - DSi::CheckNDMAs(1, 0x2B); + DSi.CheckNDMAs(1, 0x2B); } } -void Update() +void DSi_AES::Update() { if (RemExtra > 0) { @@ -525,19 +475,19 @@ void Update() } Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES); - DSi::StopNDMAs(1, 0x2A); + if (Cnt & (1<<30)) DSi.SetIRQ2(IRQ2_DSi_AES); + DSi.StopNDMAs(1, 0x2A); if (!OutputFIFO.IsEmpty()) - DSi::CheckNDMAs(1, 0x2B); + DSi.CheckNDMAs(1, 0x2B); else - DSi::StopNDMAs(1, 0x2B); + DSi.StopNDMAs(1, 0x2B); OutputFlush = false; } } -void WriteIV(u32 offset, u32 val, u32 mask) +void DSi_AES::WriteIV(u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&IV[offset]; @@ -546,7 +496,7 @@ void WriteIV(u32 offset, u32 val, u32 mask) //printf("AES: IV: "); _printhex(IV, 16); } -void WriteMAC(u32 offset, u32 val, u32 mask) +void DSi_AES::WriteMAC(u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&MAC[offset]; @@ -555,7 +505,24 @@ void WriteMAC(u32 offset, u32 val, u32 mask) //printf("AES: MAC: "); _printhex(MAC, 16); } -void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey) +void DSi_AES::ROL16(u8* val, u32 n) +{ + u32 n_coarse = n >> 3; + u32 n_fine = n & 7; + u8 tmp[16]; + + for (u32 i = 0; i < 16; i++) + { + tmp[i] = val[(i - n_coarse) & 0xF]; + } + + for (u32 i = 0; i < 16; i++) + { + val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine)); + } +} + +void DSi_AES::DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey) { const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79}; u8 tmp[16]; @@ -576,7 +543,7 @@ void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey) memcpy(normalkey, tmp, 16); } -void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask) +void DSi_AES::WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&KeyNormal[slot][offset]; @@ -585,7 +552,7 @@ void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask) //printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16); } -void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask) +void DSi_AES::WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&KeyX[slot][offset]; @@ -594,7 +561,7 @@ void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask) //printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16); } -void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) +void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&KeyY[slot][offset]; @@ -608,4 +575,4 @@ void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) } } -} +} \ No newline at end of file diff --git a/src/DSi_AES.h b/src/DSi_AES.h index f3aa550c..d83c870e 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,55 +21,96 @@ #include "types.h" #include "Savestate.h" +#include "FIFO.h" +#include "tiny-AES-c/aes.hpp" +namespace melonDS +{ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" #if defined(__GNUC__) && (__GNUC__ >= 11) // gcc 11.* // NOTE: Yes, the compiler does *not* recognize this code pattern, so it is indeed an optimization. -__attribute((always_inline)) static void Bswap128(void* Dst, void* Src) +__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { *(__int128*)Dst = __builtin_bswap128(*(__int128*)Src); } #else -__attribute((always_inline)) static void Bswap128(void* Dst, void* Src) +__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { - for (int i = 0; i < 16; ++i) - { - ((u8*)Dst)[i] = ((u8*)Src)[15 - i]; + for (int i = 0; i < 16; ++i) + { + ((u8*)Dst)[i] = ((u8*)Src)[15 - i]; } } #endif #pragma GCC diagnostic pop -namespace DSi_AES +class DSi; +class DSi_AES { +public: + DSi_AES(melonDS::DSi& dsi); + ~DSi_AES(); + void Reset(); + void DoSavestate(Savestate* file); -extern u32 Cnt; + u32 ReadCnt() const; + void WriteCnt(u32 val); + void WriteBlkCnt(u32 val); -bool Init(); -void DeInit(); -void Reset(); + u32 ReadOutputFIFO(); + void WriteInputFIFO(u32 val); + void CheckInputDMA(); + void CheckOutputDMA(); + void Update(); -void DoSavestate(Savestate* file); + void WriteIV(u32 offset, u32 val, u32 mask); + void WriteMAC(u32 offset, u32 val, u32 mask); + void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask); + void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask); + void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask); -u32 ReadCnt(); -void WriteCnt(u32 val); -void WriteBlkCnt(u32 val); + static void ROL16(u8* val, u32 n); + static void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey); -u32 ReadOutputFIFO(); -void WriteInputFIFO(u32 val); -void CheckInputDMA(); -void CheckOutputDMA(); -void Update(); +private: + melonDS::DSi& DSi; + u32 Cnt; -void WriteIV(u32 offset, u32 val, u32 mask); -void WriteMAC(u32 offset, u32 val, u32 mask); -void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask); -void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask); -void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask); + u32 BlkCnt; + u32 RemExtra; + u32 RemBlocks; -void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey); + bool OutputFlush; + + u32 InputDMASize, OutputDMASize; + u32 AESMode; + + FIFO InputFIFO; + FIFO OutputFIFO; + + u8 IV[16]; + + u8 MAC[16]; + + u8 KeyNormal[4][16]; + u8 KeyX[4][16]; + u8 KeyY[4][16]; + + u8 CurKey[16]; + u8 CurMAC[16]; + + // output MAC for CCM encrypt + u8 OutputMAC[16]; + bool OutputMACDue; + + AES_ctx Ctx; + + void ProcessBlock_CCM_Extra(); + void ProcessBlock_CCM_Decrypt(); + void ProcessBlock_CCM_Encrypt(); + void ProcessBlock_CTR(); +}; } - #endif // DSI_AES_H diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index c5fa29df..a1cdbe0a 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -16,63 +16,48 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include #include #include #include "DSi.h" #include "DSi_Camera.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace DSi_CamModule -{ - -Camera* Camera0; // 78 / facing outside -Camera* Camera1; // 7A / selfie cam - -u16 ModuleCnt; -u16 Cnt; - -u32 CropStart, CropEnd; - -// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are -u32 DataBuffer[512]; -u32 BufferReadPos, BufferWritePos; -u32 BufferNumLines; -Camera* CurCamera; // note on camera data/etc intervals // on hardware those are likely affected by several factors // namely, how long cameras take to process frames // camera IRQ is fired at roughly 15FPS with default config -const u32 kIRQInterval = 1120000; // ~30 FPS -const u32 kTransferStart = 60000; +const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS +const u32 DSi_CamModule::kTransferStart = 60000; -bool Init() +DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi) { - Camera0 = new Camera(0); - Camera1 = new Camera(1); + DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ)); + DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline)); - return true; + Camera0 = DSi.I2C.GetOuterCamera(); + Camera1 = DSi.I2C.GetInnerCamera(); } -void DeInit() +DSi_CamModule::~DSi_CamModule() { - delete Camera0; - delete Camera1; - Camera0 = nullptr; Camera1 = nullptr; + + DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0); + DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0); } -void Reset() +void DSi_CamModule::Reset() { - Camera0->Reset(); - Camera1->Reset(); - ModuleCnt = 0; // CHECKME Cnt = 0; @@ -85,16 +70,16 @@ void Reset() BufferNumLines = 0; CurCamera = nullptr; - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0); + DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0); } -void Stop() +void DSi_CamModule::Stop() { Camera0->Stop(); Camera1->Stop(); } -void DoSavestate(Savestate* file) +void DSi_CamModule::DoSavestate(Savestate* file) { file->Section("CAMi"); @@ -104,15 +89,12 @@ void DoSavestate(Savestate* file) /*file->VarArray(FrameBuffer, sizeof(FrameBuffer)); file->Var32(&TransferPos); file->Var32(&FrameLength);*/ - - Camera0->DoSavestate(file); - Camera1->DoSavestate(file); } -void IRQ(u32 param) +void DSi_CamModule::IRQ(u32 param) { - Camera* activecam = nullptr; + DSi_Camera* activecam = nullptr; // TODO: cameras don't have any priority! // activating both together will jumble the image data together @@ -124,7 +106,7 @@ void IRQ(u32 param) activecam->StartTransfer(); if (Cnt & (1<<11)) - NDS::SetIRQ(0, NDS::IRQ_DSi_Camera); + DSi.SetIRQ(0, IRQ_DSi_Camera); if (Cnt & (1<<15)) { @@ -132,14 +114,14 @@ void IRQ(u32 param) BufferWritePos = 0; BufferNumLines = 0; CurCamera = activecam; - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, TransferScanline, 0); + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0); } } - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0); + DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0); } -void TransferScanline(u32 line) +void DSi_CamModule::TransferScanline(u32 line) { u32* dstbuf = &DataBuffer[BufferWritePos]; int maxlen = 512 - BufferWritePos; @@ -162,7 +144,7 @@ void TransferScanline(u32 line) if (line < ystart || line > yend) { if (!CurCamera->TransferDone()) - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1); + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); return; } @@ -230,7 +212,7 @@ void TransferScanline(u32 line) BufferReadPos = 0; // checkme BufferWritePos = 0; BufferNumLines = 0; - DSi::CheckNDMAs(0, 0x0B); + DSi.CheckNDMAs(0, 0x0B); } else { @@ -242,11 +224,11 @@ void TransferScanline(u32 line) if (CurCamera->TransferDone()) return; - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1); + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); } -u8 Read8(u32 addr) +u8 DSi_CamModule::Read8(u32 addr) { // @@ -254,7 +236,7 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 DSi_CamModule::Read16(u32 addr) { switch (addr) { @@ -266,7 +248,7 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 DSi_CamModule::Read32(u32 addr) { switch (addr) { @@ -292,14 +274,14 @@ u32 Read32(u32 addr) return 0; } -void Write8(u32 addr, u8 val) +void DSi_CamModule::Write8(u32 addr, u8 val) { // Log(LogLevel::Debug, "unknown DSi cam write8 %08X %02X\n", addr, val); } -void Write16(u32 addr, u16 val) +void DSi_CamModule::Write16(u32 addr, u16 val) { switch (addr) { @@ -378,7 +360,7 @@ void Write16(u32 addr, u16 val) Log(LogLevel::Debug, "unknown DSi cam write16 %08X %04X\n", addr, val); } -void Write32(u32 addr, u32 val) +void DSi_CamModule::Write32(u32 addr, u32 val) { switch (addr) { @@ -397,16 +379,15 @@ void Write32(u32 addr, u32 val) -Camera::Camera(u32 num) -{ - Num = num; -} - -Camera::~Camera() +DSi_Camera::DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num) : DSi_I2CDevice(dsi, host), Num(num) { } -void Camera::DoSavestate(Savestate* file) +DSi_Camera::~DSi_Camera() +{ +} + +void DSi_Camera::DoSavestate(Savestate* file) { char magic[5] = "CAMx"; magic[3] = '0' + Num; @@ -427,7 +408,7 @@ void Camera::DoSavestate(Savestate* file) file->VarArray(MCURegs, 0x8000); } -void Camera::Reset() +void DSi_Camera::Reset() { Platform::Camera_Stop(Num); @@ -452,12 +433,12 @@ void Camera::Reset() memset(FrameBuffer, 0, (640*480/2)*sizeof(u32)); } -void Camera::Stop() +void DSi_Camera::Stop() { Platform::Camera_Stop(Num); } -bool Camera::IsActivated() +bool DSi_Camera::IsActivated() const { if (StandbyCnt & (1<<14)) return false; // standby if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled @@ -466,7 +447,7 @@ bool Camera::IsActivated() } -void Camera::StartTransfer() +void DSi_Camera::StartTransfer() { TransferY = 0; @@ -496,12 +477,12 @@ void Camera::StartTransfer() Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true); } -bool Camera::TransferDone() +bool DSi_Camera::TransferDone() const { return TransferY >= FrameHeight; } -int Camera::TransferScanline(u32* buffer, int maxlen) +int DSi_Camera::TransferScanline(u32* buffer, int maxlen) { if (TransferY >= FrameHeight) return 0; @@ -554,12 +535,12 @@ int Camera::TransferScanline(u32* buffer, int maxlen) } -void Camera::I2C_Start() +void DSi_Camera::Acquire() { DataPos = 0; } -u8 Camera::I2C_Read(bool last) +u8 DSi_Camera::Read(bool last) { u8 ret; @@ -580,7 +561,7 @@ u8 Camera::I2C_Read(bool last) return ret; } -void Camera::I2C_Write(u8 val, bool last) +void DSi_Camera::Write(u8 val, bool last) { if (DataPos < 2) { @@ -609,7 +590,7 @@ void Camera::I2C_Write(u8 val, bool last) else DataPos++; } -u16 Camera::I2C_ReadReg(u16 addr) +u16 DSi_Camera::I2C_ReadReg(u16 addr) const { switch (addr) { @@ -645,7 +626,7 @@ u16 Camera::I2C_ReadReg(u16 addr) return 0; } -void Camera::I2C_WriteReg(u16 addr, u16 val) +void DSi_Camera::I2C_WriteReg(u16 addr, u16 val) { switch (addr) { @@ -714,14 +695,14 @@ void Camera::I2C_WriteReg(u16 addr, u16 val) // TODO: not sure at all what is the accessible range // or if there is any overlap in the address range -u8 Camera::MCU_Read(u16 addr) +u8 DSi_Camera::MCU_Read(u16 addr) const { addr &= 0x7FFF; return MCURegs[addr]; } -void Camera::MCU_Write(u16 addr, u8 val) +void DSi_Camera::MCU_Write(u16 addr, u8 val) { addr &= 0x7FFF; @@ -743,7 +724,7 @@ void Camera::MCU_Write(u16 addr, u8 val) } -void Camera::InputFrame(u32* data, int width, int height, bool rgb) +void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb) { // TODO: double-buffering? @@ -815,18 +796,4 @@ void Camera::InputFrame(u32* data, int width, int height, bool rgb) } } -} - - - - - - - - - - - - - - +} \ No newline at end of file diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index bf18e597..363cea43 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,56 +21,36 @@ #include "types.h" #include "Savestate.h" +#include "DSi_I2C.h" -namespace DSi_CamModule +namespace melonDS { +class DSi; +class DSi_CamModule; -class Camera; - -extern Camera* Camera0; -extern Camera* Camera1; - -bool Init(); -void DeInit(); -void Reset(); -void Stop(); - -void DoSavestate(Savestate* file); - -void IRQ(u32 param); - -void TransferScanline(u32 line); - -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); - -class Camera +class DSi_Camera : public DSi_I2CDevice { public: - Camera(u32 num); - ~Camera(); + DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num); + ~DSi_Camera(); - void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; - void Reset(); + void Reset() override; void Stop(); - bool IsActivated(); + bool IsActivated() const; void StartTransfer(); - bool TransferDone(); + bool TransferDone() const; // lengths in words int TransferScanline(u32* buffer, int maxlen); - void I2C_Start(); - u8 I2C_Read(bool last); - void I2C_Write(u8 val, bool last); + void Acquire() override; + u8 Read(bool last) override; + void Write(u8 val, bool last) override; - void InputFrame(u32* data, int width, int height, bool rgb); + void InputFrame(const u32* data, int width, int height, bool rgb); u32 Num; @@ -79,7 +59,7 @@ private: u32 RegAddr; u16 RegData; - u16 I2C_ReadReg(u16 addr); + u16 I2C_ReadReg(u16 addr) const; void I2C_WriteReg(u16 addr, u16 val); u16 PLLDiv; @@ -92,7 +72,7 @@ private: u16 MCUAddr; u8 MCURegs[0x8000]; - u8 MCU_Read(u16 addr); + u8 MCU_Read(u16 addr) const; void MCU_Write(u16 addr, u8 val); u16 FrameWidth, FrameHeight; @@ -101,6 +81,51 @@ private: u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word }; -} +class DSi_CamModule +{ +public: + DSi_CamModule(melonDS::DSi& dsi); + ~DSi_CamModule(); + void Reset(); + void Stop(); + void DoSavestate(Savestate* file); + + const DSi_Camera* GetOuterCamera() const { return Camera0; } + DSi_Camera* GetOuterCamera() { return Camera0; } + const DSi_Camera* GetInnerCamera() const { return Camera1; } + DSi_Camera* GetInnerCamera() { return Camera1; } + + void IRQ(u32 param); + + void TransferScanline(u32 line); + + u8 Read8(u32 addr); + u16 Read16(u32 addr); + u32 Read32(u32 addr); + void Write8(u32 addr, u8 val); + void Write16(u32 addr, u16 val); + void Write32(u32 addr, u32 val); + +private: + melonDS::DSi& DSi; + DSi_Camera* Camera0; // 78 / facing outside + DSi_Camera* Camera1; // 7A / selfie cam + + u16 ModuleCnt; + u16 Cnt; + + u32 CropStart, CropEnd; + + // pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are + u32 DataBuffer[512]; + u32 BufferReadPos, BufferWritePos; + u32 BufferNumLines; + DSi_Camera* CurCamera; + + static const u32 kIRQInterval; + static const u32 kTransferStart; +}; + +} #endif // DSI_CAMERA_H diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index eaa80f9b..25abd474 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -24,37 +24,17 @@ #include "NDS.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace DSi_DSP -{ -// not sure whether to not rather put it somewhere else -u16 SNDExCnt; - -Teakra::Teakra* TeakraCore; - -bool SCFG_RST; - -u16 DSP_PADR; -u16 DSP_PCFG; -u16 DSP_PSTS; -u16 DSP_PSEM; -u16 DSP_PMASK; -u16 DSP_PCLEAR; -u16 DSP_CMD[3]; -u16 DSP_REP[3]; - -u64 DSPTimestamp; - -FIFO PDATAReadFifo/*, *PDATAWriteFifo*/; -int PDataDMALen = 0; - -constexpr u32 DataMemoryOffset = 0x20000; // from Teakra memory_interface.h +const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h // NOTE: ^ IS IN DSP WORDS, NOT IN BYTES! -u16 GetPSTS() + +u16 DSi_DSP::GetPSTS() const { u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit //r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers @@ -73,97 +53,101 @@ u16 GetPSTS() return r; } -void IrqRep0() +void DSi_DSP::IrqRep0() { - if (DSP_PCFG & (1<< 9)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + if (DSP_PCFG & (1<< 9)) DSi.SetIRQ(0, IRQ_DSi_DSP); } -void IrqRep1() +void DSi_DSP::IrqRep1() { - if (DSP_PCFG & (1<<10)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + if (DSP_PCFG & (1<<10)) DSi.SetIRQ(0, IRQ_DSi_DSP); } -void IrqRep2() +void DSi_DSP::IrqRep2() { - if (DSP_PCFG & (1<<11)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + if (DSP_PCFG & (1<<11)) DSi.SetIRQ(0, IRQ_DSi_DSP); } -void IrqSem() +void DSi_DSP::IrqSem() { DSP_PSTS |= 1<<9; // apparently these are always fired? - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + DSi.SetIRQ(0, IRQ_DSi_DSP); } -u16 DSPRead16(u32 addr) +u16 DSi_DSP::DSPRead16(u32 addr) { if (!(addr & 0x40000)) { - u8* ptr = DSi::NWRAMMap_B[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7]; return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0; } else { - u8* ptr = DSi::NWRAMMap_C[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7]; return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0; } } -void DSPWrite16(u32 addr, u16 val) +void DSi_DSP::DSPWrite16(u32 addr, u16 val) { // TODO: does the rule for overlapping NWRAM slots also apply to the DSP side? if (!(addr & 0x40000)) { - u8* ptr = DSi::NWRAMMap_B[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7]; if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val; } else { - u8* ptr = DSi::NWRAMMap_C[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7]; if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val; } } -void AudioCb(std::array frame) +void DSi_DSP::AudioCb(std::array frame) { // TODO } -bool Init() +DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi) { + DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32)); + TeakraCore = new Teakra::Teakra(); SCFG_RST = false; - if (!TeakraCore) return false; + // ???? + //if (!TeakraCore) return false; - TeakraCore->SetRecvDataHandler(0, IrqRep0); - TeakraCore->SetRecvDataHandler(1, IrqRep1); - TeakraCore->SetRecvDataHandler(2, IrqRep2); + using namespace std::placeholders; - TeakraCore->SetSemaphoreHandler(IrqSem); + TeakraCore->SetRecvDataHandler(0, std::bind(&DSi_DSP::IrqRep0, this)); + TeakraCore->SetRecvDataHandler(1, std::bind(&DSi_DSP::IrqRep1, this)); + TeakraCore->SetRecvDataHandler(2, std::bind(&DSi_DSP::IrqRep2, this)); + + TeakraCore->SetSemaphoreHandler(std::bind(&DSi_DSP::IrqSem, this)); Teakra::SharedMemoryCallback smcb; - smcb.read16 = DSPRead16; - smcb.write16 = DSPWrite16; + smcb.read16 = std::bind(&DSi_DSP::DSPRead16, this, _1); + smcb.write16 = std::bind(&DSi_DSP::DSPWrite16, this, _1, _2); TeakraCore->SetSharedMemoryCallback(smcb); // these happen instantaneously and without too much regard for bus aribtration // rules, so, this might have to be changed later on Teakra::AHBMCallback cb; - cb.read8 = DSi::ARM9Read8; - cb.write8 = DSi::ARM9Write8; - cb.read16 = DSi::ARM9Read16; - cb.write16 = DSi::ARM9Write16; - cb.read32 = DSi::ARM9Read32; - cb.write32 = DSi::ARM9Write32; + cb.read8 = [this](auto addr) { return DSi.ARM9Read8(addr); }; + cb.write8 = [this](auto addr, auto val) { DSi.ARM9Write8(addr, val); }; + cb.read16 = [this](auto addr) { return DSi.ARM9Read16(addr); }; + cb.write16 = [this](auto addr, auto val) { DSi.ARM9Write16(addr, val); }; + cb.read32 = [this](auto addr) { return DSi.ARM9Read32(addr); }; + cb.write32 = [this](auto addr, auto val) { DSi.ARM9Write32(addr, val); }; TeakraCore->SetAHBMCallback(cb); - TeakraCore->SetAudioCallback(AudioCb); + TeakraCore->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1)); //PDATAReadFifo = new FIFO(16); //PDATAWriteFifo = new FIFO(16); - - return true; } -void DeInit() + +DSi_DSP::~DSi_DSP() { //if (PDATAWriteFifo) delete PDATAWriteFifo; if (TeakraCore) delete TeakraCore; @@ -171,9 +155,11 @@ void DeInit() //PDATAReadFifo = NULL; //PDATAWriteFifo = NULL; TeakraCore = NULL; + + DSi.UnregisterEventFunc(Event_DSi_DSP, 0); } -void Reset() +void DSi_DSP::Reset() { DSPTimestamp = 0; @@ -191,46 +177,46 @@ void Reset() //PDATAWriteFifo->Clear(); TeakraCore->Reset(); - NDS::CancelEvent(NDS::Event_DSi_DSP); + DSi.CancelEvent(Event_DSi_DSP); SNDExCnt = 0; } -bool IsRstReleased() +bool DSi_DSP::IsRstReleased() const { return SCFG_RST; } -void SetRstLine(bool release) +void DSi_DSP::SetRstLine(bool release) { SCFG_RST = release; Reset(); - DSPTimestamp = NDS::ARM9Timestamp; // only start now! + DSPTimestamp = DSi.ARM9Timestamp; // only start now! } -inline bool IsDSPCoreEnabled() +inline bool DSi_DSP::IsDSPCoreEnabled() const { - return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0))); + return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0))); } -inline bool IsDSPIOEnabled() +inline bool DSi_DSP::IsDSPIOEnabled() const { - return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST; + return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST; } -bool DSPCatchUp() +bool DSi_DSP::DSPCatchUp() { //asm volatile("int3"); if (!IsDSPCoreEnabled()) { // nothing to do, but advance the current time so that we don't do an // unreasonable amount of cycles when rst is released - if (DSPTimestamp < NDS::ARM9Timestamp) - DSPTimestamp = NDS::ARM9Timestamp; + if (DSPTimestamp < DSi.ARM9Timestamp) + DSPTimestamp = DSi.ARM9Timestamp; return false; } - u64 curtime = NDS::ARM9Timestamp; + u64 curtime = DSi.ARM9Timestamp; if (DSPTimestamp >= curtime) return true; // ummmm?! @@ -245,9 +231,9 @@ bool DSPCatchUp() return true; } -void DSPCatchUpU32(u32 _) { DSPCatchUp(); } +void DSi_DSP::DSPCatchUpU32(u32 _) { DSPCatchUp(); } -void PDataDMAWrite(u16 wrval) +void DSi_DSP::PDataDMAWrite(u16 wrval) { u32 addr = DSP_PADR; @@ -271,7 +257,7 @@ void PDataDMAWrite(u16 wrval) { switch (TeakraCore->AHBMGetUnitSize(0)) { - case 0: /* 8bit */ DSi::ARM9Write8 (addr, (u8)wrval); break; + case 0: /* 8bit */ DSi.ARM9Write8 (addr, (u8)wrval); break; case 1: /* 16 b */ TeakraCore->AHBMWrite16(addr, wrval); break; // does it work like this, or should it first buffer two u16's // until it has enough data to write to the actual destination? @@ -286,10 +272,10 @@ void PDataDMAWrite(u16 wrval) if (DSP_PCFG & (1<<1)) // auto-increment ++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2? - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); // wrfifo empty + DSi.SetIRQ(0, IRQ_DSi_DSP); // wrfifo empty } // TODO: FIFO interrupts! (rd full, nonempty) -u16 PDataDMARead() +u16 DSi_DSP::PDataDMARead() { u16 r = 0; u32 addr = DSP_PADR; @@ -313,7 +299,7 @@ u16 PDataDMARead() { switch (TeakraCore->AHBMGetUnitSize(0)) { - case 0: /* 8bit */ r = DSi::ARM9Read8 (addr); break; + case 0: /* 8bit */ r = DSi.ARM9Read8 (addr); break; case 1: /* 16 b */ r = TeakraCore->AHBMRead16(addr); break; case 2: /* 32 b */ r = (u16)TeakraCore->AHBMRead32(addr); break; } @@ -327,7 +313,7 @@ u16 PDataDMARead() return r; } -void PDataDMAFetch() +void DSi_DSP::PDataDMAFetch() { if (!PDataDMALen) return; @@ -335,7 +321,7 @@ void PDataDMAFetch() if (PDataDMALen > 0) --PDataDMALen; } -void PDataDMAStart() +void DSi_DSP::PDataDMAStart() { switch ((DSP_PSTS & (3<<2)) >> 2) { @@ -351,16 +337,16 @@ void PDataDMAStart() for (int i = 0; i < amt; ++i) PDataDMAFetch(); - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + DSi.SetIRQ(0, IRQ_DSi_DSP); } -void PDataDMACancel() +void DSi_DSP::PDataDMACancel() { PDataDMALen = 0; PDATAReadFifo.Clear(); } -u16 PDataDMAReadMMIO() +u16 DSi_DSP::PDataDMAReadMMIO() { u16 ret; @@ -386,12 +372,12 @@ u16 PDataDMAReadMMIO() } if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull()) - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + DSi.SetIRQ(0, IRQ_DSi_DSP); return ret; } -u8 Read8(u32 addr) +u8 DSi_DSP::Read8(u32 addr) { //if (!IsDSPIOEnabled()) return 0; DSPCatchUp(); @@ -418,7 +404,7 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 DSi_DSP::Read16(u32 addr) { //printf("DSP READ16 %d %08X %08X\n", IsDSPCoreEnabled(), addr, NDS::GetPC(0)); //if (!IsDSPIOEnabled()) return 0; @@ -461,14 +447,14 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 DSi_DSP::Read32(u32 addr) { addr &= 0x3C; return Read16(addr); // *shrug* (doesn't do anything unintended due to the // 4byte spacing between regs while they're all 16bit) } -void Write8(u32 addr, u8 val) +void DSi_DSP::Write8(u32 addr, u8 val) { //if (!IsDSPIOEnabled()) return; DSPCatchUp(); @@ -489,9 +475,9 @@ void Write8(u32 addr, u8 val) // no REPx writes } } -void Write16(u32 addr, u16 val) +void DSi_DSP::Write16(u32 addr, u16 val) { - Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, NDS::GetPC(0)); + Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, DSi.GetPC(0)); //if (!IsDSPIOEnabled()) return; DSPCatchUp(); @@ -544,14 +530,16 @@ void Write16(u32 addr, u16 val) } } -void Write32(u32 addr, u32 val) +void DSi_DSP::Write32(u32 addr, u32 val) { addr &= 0x3C; Write16(addr, val & 0xFFFF); } -void WriteSNDExCnt(u16 val) +void DSi_DSP::WriteSNDExCnt(u16 val, u16 mask) { + val = (val & mask) | (SNDExCnt & ~mask); + // it can be written even in NDS mode // mic frequency can only be changed if it was disabled @@ -565,7 +553,7 @@ void WriteSNDExCnt(u16 val) SNDExCnt = val & 0xE00F; } -void Run(u32 cycles) +void DSi_DSP::Run(u32 cycles) { if (!IsDSPCoreEnabled()) { @@ -577,12 +565,12 @@ void Run(u32 cycles) DSPTimestamp += cycles; - NDS::CancelEvent(NDS::Event_DSi_DSP); - NDS::ScheduleEvent(NDS::Event_DSi_DSP, false, - 16384/*from citra (TeakraSlice)*/, DSPCatchUpU32, 0); + DSi.CancelEvent(Event_DSi_DSP); + DSi.ScheduleEvent(Event_DSi_DSP, false, + 16384/*from citra (TeakraSlice)*/, 0, 0); } -void DoSavestate(Savestate* file) +void DSi_DSP::DoSavestate(Savestate* file) { file->Section("DSPi"); @@ -608,5 +596,4 @@ void DoSavestate(Savestate* file) // TODO: save the Teakra state!!! } -} - +} \ No newline at end of file diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h index e88b5b56..f76b4202 100644 --- a/src/DSi_DSP.h +++ b/src/DSi_DSP.h @@ -25,50 +25,89 @@ // TODO: for actual sound output // * audio callbacks -namespace DSi_DSP +namespace Teakra { class Teakra; } + +namespace melonDS { +class DSi; +class DSi_DSP +{ +public: + DSi_DSP(melonDS::DSi& dsi); + ~DSi_DSP(); + void Reset(); + void DoSavestate(Savestate* file); -extern u16 SNDExCnt; + void DSPCatchUpU32(u32 _); -extern u16 DSP_PDATA; -extern u16 DSP_PADR; -extern u16 DSP_PCFG; -extern u16 DSP_PSTS; -extern u16 DSP_PSEM; -extern u16 DSP_PMASK; -extern u16 DSP_PCLEAR; -extern u16 DSP_SEM; -extern u16 DSP_CMD[3]; -extern u16 DSP_REP[3]; + // SCFG_RST bit0 + bool IsRstReleased() const; + void SetRstLine(bool release); -bool Init(); -void DeInit(); -void Reset(); + // DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT) + u8 Read8(u32 addr); + void Write8(u32 addr, u8 val); -void DoSavestate(Savestate* file); + u16 Read16(u32 addr); + void Write16(u32 addr, u16 val); -void DSPCatchUpU32(u32 _); + u32 Read32(u32 addr); + void Write32(u32 addr, u32 val); -// SCFG_RST bit0 -bool IsRstReleased(); -void SetRstLine(bool release); + u16 ReadSNDExCnt() const { return SNDExCnt; } + void WriteSNDExCnt(u16 val, u16 mask); -// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT) -u8 Read8(u32 addr); -void Write8(u32 addr, u8 val); + // NOTE: checks SCFG_CLK9 + void Run(u32 cycles); -u16 Read16(u32 addr); -void Write16(u32 addr, u16 val); + void IrqRep0(); + void IrqRep1(); + void IrqRep2(); + void IrqSem(); + u16 DSPRead16(u32 addr); + void DSPWrite16(u32 addr, u16 val); + void AudioCb(std::array frame); -u32 Read32(u32 addr); -void Write32(u32 addr, u32 val); +private: + melonDS::DSi& DSi; + // not sure whether to not rather put it somewhere else + u16 SNDExCnt; -void WriteSNDExCnt(u16 val); + Teakra::Teakra* TeakraCore; -// NOTE: checks SCFG_CLK9 -void Run(u32 cycles); + bool SCFG_RST; + + u16 DSP_PADR; + u16 DSP_PCFG; + u16 DSP_PSTS; + u16 DSP_PSEM; + u16 DSP_PMASK; + u16 DSP_PCLEAR; + u16 DSP_CMD[3]; + u16 DSP_REP[3]; + + u64 DSPTimestamp; + + FIFO PDATAReadFifo/*, *PDATAWriteFifo*/; + int PDataDMALen; + + static const u32 DataMemoryOffset; + + u16 GetPSTS() const; + + inline bool IsDSPCoreEnabled() const; + inline bool IsDSPIOEnabled() const; + + bool DSPCatchUp(); + + void PDataDMAWrite(u16 wrval); + u16 PDataDMARead(); + void PDataDMAFetch(); + void PDataDMAStart(); + void PDataDMACancel(); + u16 PDataDMAReadMMIO(); +}; } - #endif // DSI_DSP_H diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index c4ad50c5..28f98dc8 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -26,21 +26,21 @@ #include "SPI.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace DSi_BPTWL -{ // TODO: These are purely approximations -const double PowerButtonShutdownTime = 0.5; -const double PowerButtonForcedShutdownTime = 5.0; -const double VolumeSwitchRepeatStart = 0.5; -const double VolumeSwitchRepeatRate = 1.0 / 6; +const double DSi_BPTWL::PowerButtonShutdownTime = 0.5; +const double DSi_BPTWL::PowerButtonForcedShutdownTime = 5.0; +const double DSi_BPTWL::VolumeSwitchRepeatStart = 0.5; +const double DSi_BPTWL::VolumeSwitchRepeatRate = 1.0 / 6; // Could not find a pattern or a decent formula for these, // regardless, they're only 64 bytes in size -const u8 VolumeDownTable[32] = +const u8 DSi_BPTWL::VolumeDownTable[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A, @@ -48,7 +48,7 @@ const u8 VolumeDownTable[32] = 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, }; -const u8 VolumeUpTable[32] = +const u8 DSi_BPTWL::VolumeUpTable[32] = { 0x02, 0x03, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, @@ -56,27 +56,16 @@ const u8 VolumeUpTable[32] = 0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, }; -double PowerButtonTime = 0.0; -bool PowerButtonDownFlag = false; -bool PowerButtonShutdownFlag = false; -double VolumeSwitchTime = 0.0; -double VolumeSwitchRepeatTime = 0.0; -bool VolumeSwitchDownFlag = false; -u32 VolumeSwitchKeysDown = 0; -u8 Registers[0x100]; -u32 CurPos; - -bool Init() -{ - return true; -} - -void DeInit() +DSi_BPTWL::DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi_I2CDevice(dsi, host) { } -void Reset() +DSi_BPTWL::~DSi_BPTWL() +{ +} + +void DSi_BPTWL::Reset() { CurPos = -1; memset(Registers, 0x5A, 0x100); @@ -115,10 +104,11 @@ void Reset() VolumeSwitchTime = 0.0; VolumeSwitchRepeatTime = 0.0; VolumeSwitchKeysDown = 0; + VolumeSwitchDownFlag = false; } -void DoSavestate(Savestate* file) +void DSi_BPTWL::DoSavestate(Savestate* file) { file->Section("I2BP"); @@ -127,24 +117,24 @@ void DoSavestate(Savestate* file) } // TODO: Needs more investigation on the other bits -inline bool GetIRQMode() +inline bool DSi_BPTWL::GetIRQMode() const { return Registers[0x12] & 0x01; } -u8 GetBootFlag() { return Registers[0x70]; } +u8 DSi_BPTWL::GetBootFlag() const { return Registers[0x70]; } -bool GetBatteryCharging() { return Registers[0x20] >> 7; } -void SetBatteryCharging(bool charging) +bool DSi_BPTWL::GetBatteryCharging() const { return Registers[0x20] >> 7; } +void DSi_BPTWL::SetBatteryCharging(bool charging) { Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F)); } -u8 GetBatteryLevel() { return Registers[0x20] & 0xF; } -void SetBatteryLevel(u8 batteryLevel) +u8 DSi_BPTWL::GetBatteryLevel() const { return Registers[0x20] & 0xF; } +void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel) { Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); - SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); + //SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); if (batteryLevel <= 1) { @@ -153,20 +143,20 @@ void SetBatteryLevel(u8 batteryLevel) } -u8 GetVolumeLevel() { return Registers[0x40]; } -void SetVolumeLevel(u8 volume) +u8 DSi_BPTWL::GetVolumeLevel() const { return Registers[0x40]; } +void DSi_BPTWL::SetVolumeLevel(u8 volume) { Registers[0x40] = volume & 0x1F; } -u8 GetBacklightLevel() { return Registers[0x41]; } -void SetBacklightLevel(u8 backlight) +u8 DSi_BPTWL::GetBacklightLevel() const { return Registers[0x41]; } +void DSi_BPTWL::SetBacklightLevel(u8 backlight) { Registers[0x41] = backlight > 4 ? 4 : backlight; } -void ResetButtonState() +void DSi_BPTWL::ResetButtonState() { PowerButtonTime = 0.0; PowerButtonDownFlag = false; @@ -178,7 +168,7 @@ void ResetButtonState() VolumeSwitchRepeatTime = 0.0; } -void DoHardwareReset(bool direct) +void DSi_BPTWL::DoHardwareReset(bool direct) { ResetButtonState(); @@ -187,23 +177,23 @@ void DoHardwareReset(bool direct) if (direct) { // TODO: This doesn't seem to stop the SPU - DSi::SoftReset(); + DSi.SoftReset(); return; } // TODO: soft-reset might need to be scheduled later! // TODO: this has been moved for the JIT to work, nothing is confirmed here - NDS::ARM7->Halt(4); + DSi.ARM7.Halt(4); } -void DoShutdown() +void DSi_BPTWL::DoShutdown() { ResetButtonState(); - NDS::Stop(Platform::StopReason::PowerOff); + DSi.Stop(Platform::StopReason::PowerOff); } -void SetPowerButtonHeld(double time) +void DSi_BPTWL::SetPowerButtonHeld(double time) { if (!PowerButtonDownFlag) { @@ -230,7 +220,7 @@ void SetPowerButtonHeld(double time) } } -void SetPowerButtonReleased(double time) +void DSi_BPTWL::SetPowerButtonReleased(double time) { double elapsed = time - PowerButtonTime; if (elapsed >= 0 && elapsed < PowerButtonShutdownTime) @@ -243,12 +233,12 @@ void SetPowerButtonReleased(double time) PowerButtonShutdownFlag = false; } -void SetVolumeSwitchHeld(u32 key) +void DSi_BPTWL::SetVolumeSwitchHeld(u32 key) { VolumeSwitchKeysDown |= (1 << key); } -void SetVolumeSwitchReleased(u32 key) +void DSi_BPTWL::SetVolumeSwitchReleased(u32 key) { VolumeSwitchKeysDown &= ~(1 << key); VolumeSwitchDownFlag = false; @@ -256,7 +246,7 @@ void SetVolumeSwitchReleased(u32 key) VolumeSwitchRepeatTime = 0.0; } -inline bool CheckVolumeSwitchKeysValid() +inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() const { bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up); bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down); @@ -264,7 +254,7 @@ inline bool CheckVolumeSwitchKeysValid() return up != down; } -s32 ProcessVolumeSwitchInput(double time) +s32 DSi_BPTWL::ProcessVolumeSwitchInput(double time) { if (!CheckVolumeSwitchKeysValid()) return -1; @@ -303,7 +293,7 @@ s32 ProcessVolumeSwitchInput(double time) } -void DoPowerButtonPress() +void DSi_BPTWL::DoPowerButtonPress() { // Set button pressed IRQ SetIRQ(IRQ_PowerButtonPressed); @@ -311,7 +301,7 @@ void DoPowerButtonPress() // There is no default hardware behavior for pressing the power button } -void DoPowerButtonReset() +void DSi_BPTWL::DoPowerButtonReset() { // Reset via IRQ, handled by software SetIRQ(IRQ_PowerButtonReset); @@ -324,7 +314,7 @@ void DoPowerButtonReset() } } -void DoPowerButtonShutdown() +void DSi_BPTWL::DoPowerButtonShutdown() { // Shutdown via IRQ, handled by software if (!PowerButtonShutdownFlag) @@ -346,12 +336,12 @@ void DoPowerButtonShutdown() // down the power button, the DSi will still shut down } -void DoPowerButtonForceShutdown() +void DSi_BPTWL::DoPowerButtonForceShutdown() { DoShutdown(); } -void DoVolumeSwitchPress(u32 key) +void DSi_BPTWL::DoVolumeSwitchPress(u32 key) { u8 volume = Registers[0x40]; @@ -373,22 +363,22 @@ void DoVolumeSwitchPress(u32 key) SetIRQ(IRQ_VolumeSwitchPressed); } -void SetIRQ(u8 irqFlag) +void DSi_BPTWL::SetIRQ(u8 irqFlag) { Registers[0x10] |= irqFlag & IRQ_ValidMask; if (GetIRQMode()) { - NDS::SetIRQ2(NDS::IRQ2_DSi_BPTWL); + DSi.SetIRQ2(IRQ2_DSi_BPTWL); } } -void Start() +void DSi_BPTWL::Acquire() { //printf("BPTWL: start\n"); } -u8 Read(bool last) +u8 DSi_BPTWL::Read(bool last) { //printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1)); u8 ret = Registers[CurPos]; @@ -409,7 +399,7 @@ u8 Read(bool last) return ret; } -void Write(u8 val, bool last) +void DSi_BPTWL::Write(u8 val, bool last) { if (last) { @@ -460,51 +450,69 @@ void Write(u8 val, bool last) CurPos++; // CHECKME } + +DSi_I2CHost::DSi_I2CHost(melonDS::DSi& dsi) : DSi(dsi) +{ + BPTWL = new DSi_BPTWL(dsi, this); + Camera0 = new DSi_Camera(dsi, this, 0); + Camera1 = new DSi_Camera(dsi, this, 1); } - -namespace DSi_I2C +DSi_I2CHost::~DSi_I2CHost() { - -u8 Cnt; -u8 Data; - -u32 Device; - -bool Init() -{ - if (!DSi_BPTWL::Init()) return false; - - return true; + delete BPTWL; BPTWL = nullptr; + delete Camera0; Camera0 = nullptr; + delete Camera1; Camera1 = nullptr; } -void DeInit() -{ - DSi_BPTWL::DeInit(); -} - -void Reset() +void DSi_I2CHost::Reset() { Cnt = 0; Data = 0; - Device = -1; + CurDeviceID = 0; + CurDevice = nullptr; - DSi_BPTWL::Reset(); + BPTWL->Reset(); + Camera0->Reset(); + Camera1->Reset(); } -void DoSavestate(Savestate* file) +void DSi_I2CHost::DoSavestate(Savestate* file) { file->Section("I2Ci"); file->Var8(&Cnt); file->Var8(&Data); - file->Var32(&Device); + file->Var8(&CurDeviceID); - DSi_BPTWL::DoSavestate(file); + if (!file->Saving) + { + GetCurDevice(); + } + + BPTWL->DoSavestate(file); + Camera0->DoSavestate(file); + Camera1->DoSavestate(file); } -void WriteCnt(u8 val) +void DSi_I2CHost::GetCurDevice() +{ + switch (CurDeviceID) + { + case 0x4A: CurDevice = BPTWL; break; + case 0x78: CurDevice = Camera0; break; + case 0x7A: CurDevice = Camera1; break; + case 0xA0: + case 0xE0: CurDevice = nullptr; break; + default: + Log(LogLevel::Warn, "I2C: unknown device %02X\n", CurDeviceID); + CurDevice = nullptr; + break; + } +} + +void DSi_I2CHost::WriteCnt(u8 val) { //printf("I2C: write CNT %02X, %02X, %08X\n", val, Data, NDS::GetPC(1)); @@ -522,17 +530,13 @@ void WriteCnt(u8 val) // read val &= 0xF7; - switch (Device) + if (CurDevice) + { + Data = CurDevice->Read(islast); + } + else { - case 0x4A: Data = DSi_BPTWL::Read(islast); break; - case 0x78: Data = DSi_CamModule::Camera0->I2C_Read(islast); break; - case 0x7A: Data = DSi_CamModule::Camera1->I2C_Read(islast); break; - case 0xA0: - case 0xE0: Data = 0xFF; break; - default: - Log(LogLevel::Warn, "I2C: read on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, 0, islast); Data = 0xFF; - break; } //printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast); @@ -545,37 +549,30 @@ void WriteCnt(u8 val) if (val & (1<<1)) { - Device = Data & 0xFE; + CurDeviceID = Data & 0xFE; //printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device); - switch (Device) + GetCurDevice(); + if (CurDevice) + { + CurDevice->Acquire(); + } + else { - case 0x4A: DSi_BPTWL::Start(); break; - case 0x78: DSi_CamModule::Camera0->I2C_Start(); break; - case 0x7A: DSi_CamModule::Camera1->I2C_Start(); break; - case 0xA0: - case 0xE0: ack = false; break; - default: - Log(LogLevel::Warn, "I2C: %s start on unknown device %02X\n", (Data&0x01)?"read":"write", Device); ack = false; - break; } } else { //printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast); - switch (Device) + if (CurDevice) + { + CurDevice->Write(Data, islast); + } + else { - case 0x4A: DSi_BPTWL::Write(Data, islast); break; - case 0x78: DSi_CamModule::Camera0->I2C_Write(Data, islast); break; - case 0x7A: DSi_CamModule::Camera1->I2C_Write(Data, islast); break; - case 0xA0: - case 0xE0: ack = false; break; - default: - Log(LogLevel::Warn, "I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast); ack = false; - break; } } @@ -588,14 +585,14 @@ void WriteCnt(u8 val) Cnt = val; } -u8 ReadData() +u8 DSi_I2CHost::ReadData() { return Data; } -void WriteData(u8 val) +void DSi_I2CHost::WriteData(u8 val) { Data = val; } -} +} \ No newline at end of file diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 8a54a2cd..5dfeebd0 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,95 +22,167 @@ #include "types.h" #include "Savestate.h" -namespace DSi_BPTWL +namespace melonDS { - -u8 GetBootFlag(); - -bool GetBatteryCharging(); -void SetBatteryCharging(bool charging); - -enum +class DSi_I2CHost; +class DSi_Camera; +class DSi; +class DSi_I2CDevice { - batteryLevel_Critical = 0x0, - batteryLevel_AlmostEmpty = 0x1, - batteryLevel_Low = 0x3, - batteryLevel_Half = 0x7, - batteryLevel_ThreeQuarters = 0xB, - batteryLevel_Full = 0xF +public: + DSi_I2CDevice(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi(dsi), Host(host) {} + virtual ~DSi_I2CDevice() {} + virtual void Reset() = 0; + virtual void DoSavestate(Savestate* file) = 0; + + virtual void Acquire() = 0; + virtual u8 Read(bool last) = 0; + virtual void Write(u8 val, bool last) = 0; + +protected: + melonDS::DSi& DSi; + DSi_I2CHost* Host; }; -u8 GetBatteryLevel(); -void SetBatteryLevel(u8 batteryLevel); - -// 0-31 -u8 GetVolumeLevel(); -void SetVolumeLevel(u8 volume); - -// 0-4 -u8 GetBacklightLevel(); -void SetBacklightLevel(u8 backlight); - -void DoHardwareReset(bool direct); -void DoShutdown(); - -enum +class DSi_BPTWL : public DSi_I2CDevice { - volumeKey_Up, - volumeKey_Down, +public: + + enum + { + batteryLevel_Critical = 0x0, + batteryLevel_AlmostEmpty = 0x1, + batteryLevel_Low = 0x3, + batteryLevel_Half = 0x7, + batteryLevel_ThreeQuarters = 0xB, + batteryLevel_Full = 0xF + }; + + enum + { + volumeKey_Up, + volumeKey_Down, + }; + + enum + { + IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly + IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second + IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button + IRQ_BatteryEmpty = 0x10, // + IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1 + IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down + /* + Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off) + It is unknown whether it is set as the console powers off immediately. + Bit 7 (0x80) is unused? + Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?). + */ + IRQ_ValidMask = 0x7B, + }; + + DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host); + ~DSi_BPTWL() override; + void Reset() override; + void DoSavestate(Savestate* file) override; + + u8 GetBootFlag() const; + + bool GetBatteryCharging() const; + void SetBatteryCharging(bool charging); + + u8 GetBatteryLevel() const; + void SetBatteryLevel(u8 batteryLevel); + + // 0-31 + u8 GetVolumeLevel() const; + void SetVolumeLevel(u8 volume); + + // 0-4 + u8 GetBacklightLevel() const; + void SetBacklightLevel(u8 backlight); + + void DoHardwareReset(bool direct); + void DoShutdown(); + + // Used by hotkeys + void SetPowerButtonHeld(double time); + void SetPowerButtonReleased(double time); + void SetVolumeSwitchHeld(u32 key); + void SetVolumeSwitchReleased(u32 key); + s32 ProcessVolumeSwitchInput(double time); + + void DoPowerButtonPress(); + void DoPowerButtonReset(); + void DoPowerButtonShutdown(); + void DoPowerButtonForceShutdown(); + void DoVolumeSwitchPress(u32 key); + + void SetIRQ(u8 irqFlag); + + void Acquire() override; + u8 Read(bool last) override; + void Write(u8 val, bool last) override; + +private: + static const double PowerButtonShutdownTime; + static const double PowerButtonForcedShutdownTime; + static const double VolumeSwitchRepeatStart; + static const double VolumeSwitchRepeatRate; + + static const u8 VolumeDownTable[32]; + static const u8 VolumeUpTable[32]; + + double PowerButtonTime; + bool PowerButtonDownFlag; + bool PowerButtonShutdownFlag; + double VolumeSwitchTime; + double VolumeSwitchRepeatTime; + bool VolumeSwitchDownFlag ; + u32 VolumeSwitchKeysDown; + + u8 Registers[0x100]; + u32 CurPos; + + bool GetIRQMode() const; + + void ResetButtonState(); + bool CheckVolumeSwitchKeysValid() const; }; -// Used by hotkeys -void SetPowerButtonHeld(double time); -void SetPowerButtonReleased(double time); -void SetVolumeSwitchHeld(u32 key); -void SetVolumeSwitchReleased(u32 key); -s32 ProcessVolumeSwitchInput(double time); -void DoPowerButtonPress(); -void DoPowerButtonReset(); -void DoPowerButtonShutdown(); -void DoPowerButtonForceShutdown(); -void DoVolumeSwitchPress(u32 key); - -enum +class DSi_I2CHost { - IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly - IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second - IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button - IRQ_BatteryEmpty = 0x10, // - IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1 - IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down - /* - Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off) - It is unknown whether it is set as the console powers off immediately. - Bit 7 (0x80) is unused? - Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?). - */ - IRQ_ValidMask = 0x7B, -}; +public: + DSi_I2CHost(melonDS::DSi& dsi); + ~DSi_I2CHost(); + void Reset(); + void DoSavestate(Savestate* file); -void SetIRQ(u8 irqFlag); + DSi_BPTWL* GetBPTWL() { return BPTWL; } + DSi_Camera* GetOuterCamera() { return Camera0; } + DSi_Camera* GetInnerCamera() { return Camera1; } + + u8 ReadCnt() { return Cnt; } + void WriteCnt(u8 val); + + u8 ReadData(); + void WriteData(u8 val); + +private: + melonDS::DSi& DSi; + u8 Cnt; + u8 Data; + + DSi_BPTWL* BPTWL; // 4A / BPTWL IC + DSi_Camera* Camera0; // 78 / facing outside + DSi_Camera* Camera1; // 7A / selfie cam + + u8 CurDeviceID; + DSi_I2CDevice* CurDevice; + + void GetCurDevice(); +}; } - -namespace DSi_I2C -{ - -extern u8 Cnt; - -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); - -void WriteCnt(u8 val); - -u8 ReadData(); -void WriteData(u8 val); - -//void TransferDone(u32 param); - -} - #endif // DSI_I2C_H diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index df9cdd2b..8da02540 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,6 +22,7 @@ #include "DSi.h" #include "DSi_AES.h" #include "DSi_NAND.h" +#include "FATIO.h" #include "Platform.h" #include "sha1/sha1.hpp" @@ -29,83 +30,21 @@ #include "fatfs/ff.h" -using namespace Platform; +using namespace melonDS::Platform; -namespace DSi_NAND +namespace melonDS::DSi_NAND { -FileHandle* CurFile; -FATFS CurFS; - -u8 eMMC_CID[16]; -u64 ConsoleID; - -u8 FATIV[16]; -u8 FATKey[16]; - -u8 ESKey[16]; - - -UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num); -UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num); - - -bool Init(u8* es_keyY) +NANDImage::NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept : NANDImage(nandfile, es_keyY.data()) { - CurFile = nullptr; - - std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath); - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - if ((!nandfile) && (Platform::InstanceID() > 0)) - { - FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); - if (!orig) - { - Log(LogLevel::Error, "Failed to open DSi NAND\n"); - return false; - } - - long len = FileLength(orig); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWrite); - if (nandfile) - { - u8* tmpbuf = new u8[0x10000]; - for (long i = 0; i < len; i+=0x10000) - { - long blklen = 0x10000; - if ((i+blklen) > len) blklen = len-i; - - FileRead(tmpbuf, blklen, 1, orig); - FileWrite(tmpbuf, blklen, 1, nandfile); - } - delete[] tmpbuf; - } - - Platform::CloseFile(orig); - Platform::CloseFile(nandfile); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - } +} +NANDImage::NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept +{ if (!nandfile) - return false; + return; - u64 nandlen = FileLength(nandfile); - - ff_disk_open(FF_ReadNAND, FF_WriteNAND, (LBA_t)(nandlen>>9)); - - FRESULT res; - res = f_mount(&CurFS, "0:", 0); - if (res != FR_OK) - { - Log(LogLevel::Error, "NAND mounting failed: %d\n", res); - f_unmount("0:"); - ff_disk_close(); - return false; - } + Length = FileLength(nandfile); // read the nocash footer @@ -113,26 +52,24 @@ bool Init(u8* es_keyY) char nand_footer[16]; const char* nand_footer_ref = "DSi eMMC CID/CPU"; - FileRead(nand_footer, 1, 16, nandfile); - if (memcmp(nand_footer, nand_footer_ref, 16)) + FileRead(nand_footer, 1, sizeof(nand_footer), nandfile); + if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer))) { // There is another copy of the footer at 000FF800h for the case // that by external tools the image was cut off // See https://problemkaputt.de/gbatek.htm#dsisdmmcimages FileSeek(nandfile, 0x000FF800, FileSeekOrigin::Start); - FileRead(nand_footer, 1, 16, nandfile); - if (memcmp(nand_footer, nand_footer_ref, 16)) + FileRead(nand_footer, 1, sizeof(nand_footer), nandfile); + if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer))) { Log(LogLevel::Error, "ERROR: NAND missing nocash footer\n"); CloseFile(nandfile); - f_unmount("0:"); - ff_disk_close(); - return false; + return; } } - FileRead(eMMC_CID, 1, 16, nandfile); - FileRead(&ConsoleID, 1, 8, nandfile); + FileRead(eMMC_CID.data(), 1, sizeof(eMMC_CID), nandfile); + FileRead(&ConsoleID, 1, sizeof(ConsoleID), nandfile); // init NAND crypto @@ -141,10 +78,10 @@ bool Init(u8* es_keyY) u8 keyX[16], keyY[16]; SHA1Init(&sha); - SHA1Update(&sha, eMMC_CID, 16); + SHA1Update(&sha, eMMC_CID.data(), sizeof(eMMC_CID)); SHA1Final(tmp, &sha); - Bswap128(FATIV, tmp); + Bswap128(FATIV.data(), tmp); *(u32*)&keyX[0] = (u32)ConsoleID; *(u32*)&keyX[4] = (u32)ConsoleID ^ 0x24EE6906; @@ -157,7 +94,7 @@ bool Init(u8* es_keyY) *(u32*)&keyY[12] = 0xE1A00005; DSi_AES::DeriveNormalKey(keyX, keyY, tmp); - Bswap128(FATKey, tmp); + Bswap128(FATKey.data(), tmp); *(u32*)&keyX[0] = 0x4E00004A; @@ -165,42 +102,92 @@ bool Init(u8* es_keyY) *(u32*)&keyX[8] = (u32)(ConsoleID >> 32) ^ 0xC80C4B72; *(u32*)&keyX[12] = (u32)ConsoleID; - memcpy(keyY, es_keyY, 16); + memcpy(keyY, es_keyY, sizeof(keyY)); DSi_AES::DeriveNormalKey(keyX, keyY, tmp); - Bswap128(ESKey, tmp); + Bswap128(ESKey.data(), tmp); CurFile = nandfile; - return true; } -void DeInit() +NANDImage::~NANDImage() { - f_unmount("0:"); - ff_disk_close(); - if (CurFile) CloseFile(CurFile); CurFile = nullptr; } - -FileHandle* GetFile() +NANDImage::NANDImage(NANDImage&& other) noexcept : + CurFile(other.CurFile), + eMMC_CID(other.eMMC_CID), + ConsoleID(other.ConsoleID), + FATIV(other.FATIV), + FATKey(other.FATKey), + ESKey(other.ESKey), + Length(other.Length) { - return CurFile; + other.CurFile = nullptr; +} + +NANDImage& NANDImage::operator=(NANDImage&& other) noexcept +{ + if (this != &other) + { + if (CurFile) + CloseFile(CurFile); + + CurFile = other.CurFile; + eMMC_CID = other.eMMC_CID; + ConsoleID = other.ConsoleID; + FATIV = other.FATIV; + FATKey = other.FATKey; + ESKey = other.ESKey; + Length = other.Length; + + other.CurFile = nullptr; + } + + return *this; +} + +NANDMount::NANDMount(NANDImage& nand) noexcept : Image(&nand) +{ + if (!nand) + return; + + CurFS = std::make_unique(); + ff_disk_open( + [this](BYTE* buf, LBA_t sector, UINT num) { + return this->FF_ReadNAND(buf, sector, num); + }, + [this](const BYTE* buf, LBA_t sector, UINT num) { + return this->FF_WriteNAND(buf, sector, num); + }, + (LBA_t)(nand.GetLength()>>9) + ); + + FRESULT res; + res = f_mount(CurFS.get(), "0:", 0); + if (res != FR_OK) + { + Log(LogLevel::Error, "NAND mounting failed: %d\n", res); + f_unmount("0:"); + ff_disk_close(); + return; + } } -void GetIDs(u8* emmc_cid, u64& consoleid) +NANDMount::~NANDMount() noexcept { - memcpy(emmc_cid, eMMC_CID, 16); - consoleid = ConsoleID; + f_unmount("0:"); + ff_disk_close(); } -void SetupFATCrypto(AES_ctx* ctx, u32 ctr) +void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr) { u8 iv[16]; - memcpy(iv, FATIV, sizeof(iv)); + memcpy(iv, FATIV.data(), sizeof(iv)); u32 res; res = iv[15] + (ctr & 0xFF); @@ -218,10 +205,10 @@ void SetupFATCrypto(AES_ctx* ctx, u32 ctr) else break; } - AES_init_ctx_iv(ctx, FATKey, iv); + AES_init_ctx_iv(ctx, FATKey.data(), iv); } -u32 ReadFATBlock(u64 addr, u32 len, u8* buf) +u32 NANDImage::ReadFATBlock(u64 addr, u32 len, u8* buf) { u32 ctr = (u32)(addr >> 4); @@ -243,7 +230,7 @@ u32 ReadFATBlock(u64 addr, u32 len, u8* buf) return len; } -u32 WriteFATBlock(u64 addr, u32 len, u8* buf) +u32 NANDImage::WriteFATBlock(u64 addr, u32 len, const u8* buf) { u32 ctr = (u32)(addr >> 4); @@ -272,30 +259,30 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf) } -UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num) +UINT NANDMount::FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num) { // TODO: allow selecting other partitions? u64 baseaddr = 0x10EE00; u64 blockaddr = baseaddr + (sector * 0x200ULL); - u32 res = ReadFATBlock(blockaddr, num*0x200, buf); + u32 res = Image->ReadFATBlock(blockaddr, num*0x200, buf); return res >> 9; } -UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num) +UINT NANDMount::FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num) { // TODO: allow selecting other partitions? u64 baseaddr = 0x10EE00; u64 blockaddr = baseaddr + (sector * 0x200ULL); - u32 res = WriteFATBlock(blockaddr, num*0x200, buf); + u32 res = Image->WriteFATBlock(blockaddr, num*0x200, buf); return res >> 9; } -bool ESEncrypt(u8* data, u32 len) +bool NANDImage::ESEncrypt(u8* data, u32 len) const { AES_ctx ctx; u8 iv[16]; @@ -307,7 +294,7 @@ bool ESEncrypt(u8* data, u32 len) iv[14] = 0x00; iv[15] = 0x01; - AES_init_ctx_iv(&ctx, ESKey, iv); + AES_init_ctx_iv(&ctx, ESKey.data(), iv); u32 blklen = (len + 0xF) & ~0xF; mac[0] = 0x3A; @@ -380,7 +367,7 @@ bool ESEncrypt(u8* data, u32 len) return true; } -bool ESDecrypt(u8* data, u32 len) +bool NANDImage::ESDecrypt(u8* data, u32 len) const { AES_ctx ctx; u8 iv[16]; @@ -392,7 +379,7 @@ bool ESDecrypt(u8* data, u32 len) iv[14] = 0x00; iv[15] = 0x01; - AES_init_ctx_iv(&ctx, ESKey, iv); + AES_init_ctx_iv(&ctx, ESKey.data(), iv); u32 blklen = (len + 0xF) & ~0xF; mac[0] = 0x3A; @@ -485,30 +472,43 @@ bool ESDecrypt(u8* data, u32 len) return true; } - -void ReadHardwareInfo(u8* dataS, u8* dataN) +bool NANDMount::ReadSerialData(DSiSerialData& dataS) { FF_FIL file; - FRESULT res; - u32 nread; + FRESULT res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ); - res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ); if (res == FR_OK) { - f_read(&file, dataS, 0xA4, &nread); + u32 nread; + f_read(&file, &dataS, sizeof(DSiSerialData), &nread); f_close(&file); } - res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ); - if (res == FR_OK) - { - f_read(&file, dataN, 0x9C, &nread); - f_close(&file); - } + return res == FR_OK; } +bool NANDMount::ReadHardwareInfoN(DSiHardwareInfoN& dataN) +{ + FF_FIL file; + FRESULT res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ); -void ReadUserData(u8* data) + if (res == FR_OK) + { + u32 nread; + f_read(&file, dataN.data(), sizeof(dataN), &nread); + f_close(&file); + } + + return res == FR_OK; +} + +void NANDMount::ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) +{ + ReadSerialData(dataS); + ReadHardwareInfoN(dataN); +} + +bool NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) { FF_FIL file; FRESULT res; @@ -539,7 +539,7 @@ void ReadUserData(u8* data) v2 = tmp; } - if (v1 < 0 && v2 < 0) return; + if (v1 < 0 && v2 < 0) return false; if (v2 > v1) { @@ -553,82 +553,42 @@ void ReadUserData(u8* data) } f_lseek(&file, 0); - f_read(&file, data, 0x1B0, &nread); + f_read(&file, &data, sizeof(DSiFirmwareSystemSettings), &nread); f_close(&file); + + return true; } -void PatchUserData() +static bool SaveUserData(const char* filename, const DSiFirmwareSystemSettings& data) { - FRESULT res; - - for (int i = 0; i < 2; i++) + FF_FIL file; + if (FRESULT res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE); res != FR_OK) { - char filename[64]; - sprintf(filename, "0:/shared1/TWLCFG%d.dat", i); - - FF_FIL file; - res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE); - if (res != FR_OK) - { - Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res); - continue; - } - - u8 contents[0x1B0]; - u32 nres; - f_lseek(&file, 0); - f_read(&file, contents, 0x1B0, &nres); - - // override user settings, if needed - if (Platform::GetConfigBool(Platform::Firm_OverrideSettings)) - { - // setting up username - std::string orig_username = Platform::GetConfigString(Platform::Firm_Username); - std::u16string username = std::wstring_convert, char16_t>{}.from_bytes(orig_username); - size_t usernameLength = std::min(username.length(), (size_t) 10); - memset(contents + 0xD0, 0, 11 * sizeof(char16_t)); - memcpy(contents + 0xD0, username.data(), usernameLength * sizeof(char16_t)); - - // setting language - contents[0x8E] = Platform::GetConfigInt(Platform::Firm_Language); - - // setting up color - contents[0xCC] = Platform::GetConfigInt(Platform::Firm_Color); - - // setting up birthday - contents[0xCE] = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); - contents[0xCF] = Platform::GetConfigInt(Platform::Firm_BirthdayDay); - - // setup message - std::string orig_message = Platform::GetConfigString(Platform::Firm_Message); - std::u16string message = std::wstring_convert, char16_t>{}.from_bytes(orig_message); - size_t messageLength = std::min(message.length(), (size_t) 26); - memset(contents + 0xE6, 0, 27 * sizeof(char16_t)); - memcpy(contents + 0xE6, message.data(), messageLength * sizeof(char16_t)); - - // TODO: make other items configurable? - } - - // fix touchscreen coords - *(u16*)&contents[0xB8] = 0; - *(u16*)&contents[0xBA] = 0; - contents[0xBC] = 0; - contents[0xBD] = 0; - *(u16*)&contents[0xBE] = 255<<4; - *(u16*)&contents[0xC0] = 191<<4; - contents[0xC2] = 255; - contents[0xC3] = 191; - - SHA1_CTX sha; - SHA1Init(&sha); - SHA1Update(&sha, &contents[0x88], 0x128); - SHA1Final(&contents[0], &sha); - - f_lseek(&file, 0); - f_write(&file, contents, 0x1B0, &nres); - - f_close(&file); + Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res); + return false; } + // TODO: If the file couldn't be opened, try creating a new one in its place + // (after all, we have the data for that) + + u32 bytes_written = 0; + FRESULT res = f_write(&file, &data, sizeof(DSiFirmwareSystemSettings), &bytes_written); + f_close(&file); + + if (res != FR_OK || bytes_written != sizeof(DSiFirmwareSystemSettings)) + { + Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res); + return false; + } + + return true; +} + +bool NANDMount::ApplyUserData(const DSiFirmwareSystemSettings& data) +{ + bool ok0 = SaveUserData("0:/shared1/TWLCFG0.dat", data); + bool ok1 = SaveUserData("0:/shared1/TWLCFG1.dat", data); + + return ok0 && ok1; } @@ -648,7 +608,7 @@ void debug_listfiles(const char* path) if (!info.fname[0]) break; char fullname[512]; - sprintf(fullname, "%s/%s", path, info.fname); + snprintf(fullname, sizeof(fullname), "%s/%s", path, info.fname); Log(LogLevel::Debug, "[%c] %s\n", (info.fattrib&AM_DIR)?'D':'F', fullname); if (info.fattrib & AM_DIR) @@ -660,7 +620,7 @@ void debug_listfiles(const char* path) f_closedir(&dir); } -bool ImportFile(const char* path, const u8* data, size_t len) +bool NANDMount::ImportFile(const char* path, const u8* data, size_t len) { if (!data || !len || !path) return false; @@ -694,24 +654,21 @@ bool ImportFile(const char* path, const u8* data, size_t len) return true; } -bool ImportFile(const char* path, const char* in) +bool NANDMount::ImportFile(const char* path, const char* in) { FF_FIL file; - FILE* fin; FRESULT res; - fin = fopen(in, "rb"); + Platform::FileHandle* fin = OpenLocalFile(in, FileMode::Read); if (!fin) return false; - fseek(fin, 0, SEEK_END); - u32 len = (u32)ftell(fin); - fseek(fin, 0, SEEK_SET); + u32 len = FileLength(fin); res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { - fclose(fin); + CloseFile(fin); return false; } @@ -725,11 +682,11 @@ bool ImportFile(const char* path, const char* in) blocklen = sizeof(buf); u32 nwrite; - fread(buf, blocklen, 1, fin); + FileRead(buf, blocklen, 1, fin); f_write(&file, buf, blocklen, &nwrite); } - fclose(fin); + CloseFile(fin); f_close(&file); Log(LogLevel::Debug, "Imported file from %s to %s\n", in, path); @@ -737,10 +694,9 @@ bool ImportFile(const char* path, const char* in) return true; } -bool ExportFile(const char* path, const char* out) +bool NANDMount::ExportFile(const char* path, const char* out) { FF_FIL file; - FILE* fout; FRESULT res; res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ); @@ -749,7 +705,7 @@ bool ExportFile(const char* path, const char* out) u32 len = f_size(&file); - fout = fopen(out, "wb"); + Platform::FileHandle* fout = OpenLocalFile(out, FileMode::Write); if (!fout) { f_close(&file); @@ -767,10 +723,10 @@ bool ExportFile(const char* path, const char* out) u32 nread; f_read(&file, buf, blocklen, &nread); - fwrite(buf, blocklen, 1, fout); + FileWrite(buf, blocklen, 1, fout); } - fclose(fout); + CloseFile(fout); f_close(&file); Log(LogLevel::Debug, "Exported file from %s to %s\n", path, out); @@ -778,7 +734,7 @@ bool ExportFile(const char* path, const char* out) return true; } -void RemoveFile(const char* path) +void NANDMount::RemoveFile(const char* path) { FF_FILINFO info; FRESULT res = f_stat(path, &info); @@ -791,7 +747,7 @@ void RemoveFile(const char* path) Log(LogLevel::Debug, "Removed file at %s\n", path); } -void RemoveDir(const char* path) +void NANDMount::RemoveDir(const char* path) { FF_DIR dir; FF_FILINFO info; @@ -816,7 +772,7 @@ void RemoveDir(const char* path) if (!info.fname[0]) break; char fullname[512]; - sprintf(fullname, "%s/%s", path, info.fname); + snprintf(fullname, sizeof(fullname), "%s/%s", path, info.fname); if (info.fattrib & AM_RDO) f_chmod(path, 0, AM_RDO); @@ -846,11 +802,11 @@ void RemoveDir(const char* path) } -u32 GetTitleVersion(u32 category, u32 titleid) +u32 NANDMount::GetTitleVersion(u32 category, u32 titleid) { FRESULT res; char path[256]; - sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid); + snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid); FF_FIL file; res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ); if (res != FR_OK) @@ -866,13 +822,13 @@ u32 GetTitleVersion(u32 category, u32 titleid) return version; } -void ListTitles(u32 category, std::vector& titlelist) +void NANDMount::ListTitles(u32 category, std::vector& titlelist) { FRESULT res; FF_DIR titledir; char path[256]; - sprintf(path, "0:/title/%08x", category); + snprintf(path, sizeof(path), "0:/title/%08x", category); res = f_opendir(&titledir, path); if (res != FR_OK) { @@ -898,7 +854,7 @@ void ListTitles(u32 category, std::vector& titlelist) if (version == 0xFFFFFFFF) continue; - sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version); + snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/%08x.app", category, titleid, version); FF_FILINFO appinfo; res = f_stat(path, &appinfo); if (res != FR_OK) @@ -915,16 +871,16 @@ void ListTitles(u32 category, std::vector& titlelist) f_closedir(&titledir); } -bool TitleExists(u32 category, u32 titleid) +bool NANDMount::TitleExists(u32 category, u32 titleid) { char path[256]; - sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid); + snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid); FRESULT res = f_stat(path, nullptr); return (res == FR_OK); } -void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner) +void NANDMount::GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner) { version = GetTitleVersion(category, titleid); if (version == 0xFFFFFFFF) @@ -933,7 +889,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND FRESULT res; char path[256]; - sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version); + snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/%08x.app", category, titleid, version); FF_FIL file; res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ); if (res != FR_OK) @@ -960,7 +916,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND } -bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) +bool NANDMount::CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) { FF_FIL file; FRESULT res; @@ -986,7 +942,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) memset(&ticket[0x222], 0xFF, 0x20); - ESEncrypt(ticket, 0x2A4); + Image->ESEncrypt(ticket, 0x2A4); f_write(&file, ticket, 0x2C4, &nwrite); @@ -995,7 +951,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) return true; } -bool CreateSaveFile(const char* path, u32 len) +bool NANDMount::CreateSaveFile(const char* path, u32 len) { if (len == 0) return true; if (len < 0x200) return false; @@ -1085,7 +1041,7 @@ bool CreateSaveFile(const char* path, u32 len) return true; } -bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly) { u32 titleid0 = tmd.GetCategory(); u32 titleid1 = tmd.GetID(); @@ -1098,10 +1054,10 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat u32 nwrite; // ticket - sprintf(fname, "0:/ticket/%08x", titleid0); + snprintf(fname, sizeof(fname), "0:/ticket/%08x", titleid0); f_mkdir(fname); - sprintf(fname, "0:/ticket/%08x/%08x.tik", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/ticket/%08x/%08x.tik", titleid0, titleid1); if (!CreateTicket(fname, tmd.GetCategoryNoByteswap(), tmd.GetIDNoByteswap(), header.ROMVersion)) return false; @@ -1109,29 +1065,29 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat // folder - sprintf(fname, "0:/title/%08x", titleid0); + snprintf(fname, sizeof(fname), "0:/title/%08x", titleid0); f_mkdir(fname); - sprintf(fname, "0:/title/%08x/%08x", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x", titleid0, titleid1); f_mkdir(fname); - sprintf(fname, "0:/title/%08x/%08x/content", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content", titleid0, titleid1); f_mkdir(fname); - sprintf(fname, "0:/title/%08x/%08x/data", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data", titleid0, titleid1); f_mkdir(fname); // data - sprintf(fname, "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1); if (!CreateSaveFile(fname, header.DSiPublicSavSize)) return false; - sprintf(fname, "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1); if (!CreateSaveFile(fname, header.DSiPrivateSavSize)) return false; if (header.AppFlags & 0x04) { // custom banner file - sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1); res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { @@ -1148,7 +1104,7 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat // TMD - sprintf(fname, "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1); res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { @@ -1165,14 +1121,14 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat return true; } -bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly) { NDSHeader header {}; { - FILE* f = fopen(appfile, "rb"); + Platform::FileHandle* f = OpenLocalFile(appfile, FileMode::Read); if (!f) return false; - fread(&header, sizeof(header), 1, f); - fclose(f); + FileRead(&header, sizeof(header), 1, f); + CloseFile(f); } u32 version = tmd.Contents.GetVersion(); @@ -1191,7 +1147,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re // executable char fname[128]; - sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version); if (!ImportFile(fname, appfile)) { Log(LogLevel::Error, "ImportTitle: failed to create executable\n"); @@ -1203,7 +1159,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re return true; } -bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly) { if (!app || appLength < sizeof(NDSHeader)) return false; @@ -1227,7 +1183,7 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& // executable char fname[128]; - sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version); if (!ImportFile(fname, app, appLength)) { Log(LogLevel::Error, "ImportTitle: failed to create executable\n"); @@ -1239,18 +1195,18 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& return true; } -void DeleteTitle(u32 category, u32 titleid) +void NANDMount::DeleteTitle(u32 category, u32 titleid) { char fname[128]; - sprintf(fname, "0:/ticket/%08x/%08x.tik", category, titleid); + snprintf(fname, sizeof(fname), "0:/ticket/%08x/%08x.tik", category, titleid); RemoveFile(fname); - sprintf(fname, "0:/title/%08x/%08x", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x", category, titleid); RemoveDir(fname); } -u32 GetTitleDataMask(u32 category, u32 titleid) +u32 NANDMount::GetTitleDataMask(u32 category, u32 titleid) { u32 version; NDSHeader header; @@ -1267,22 +1223,22 @@ u32 GetTitleDataMask(u32 category, u32 titleid) return ret; } -bool ImportTitleData(u32 category, u32 titleid, int type, const char* file) +bool NANDMount::ImportTitleData(u32 category, u32 titleid, int type, const char* file) { char fname[128]; switch (type) { case TitleData_PublicSav: - sprintf(fname, "0:/title/%08x/%08x/data/public.sav", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", category, titleid); break; case TitleData_PrivateSav: - sprintf(fname, "0:/title/%08x/%08x/data/private.sav", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", category, titleid); break; case TitleData_BannerSav: - sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", category, titleid); break; default: @@ -1293,22 +1249,22 @@ bool ImportTitleData(u32 category, u32 titleid, int type, const char* file) return ImportFile(fname, file); } -bool ExportTitleData(u32 category, u32 titleid, int type, const char* file) +bool NANDMount::ExportTitleData(u32 category, u32 titleid, int type, const char* file) { char fname[128]; switch (type) { case TitleData_PublicSav: - sprintf(fname, "0:/title/%08x/%08x/data/public.sav", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", category, titleid); break; case TitleData_PrivateSav: - sprintf(fname, "0:/title/%08x/%08x/data/private.sav", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", category, titleid); break; case TitleData_BannerSav: - sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", category, titleid); + snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", category, titleid); break; default: @@ -1318,4 +1274,12 @@ bool ExportTitleData(u32 category, u32 titleid, int type, const char* file) return ExportFile(fname, file); } +void DSiFirmwareSystemSettings::UpdateHash() +{ + SHA1_CTX sha; + SHA1Init(&sha); + SHA1Update(&sha, &Bytes[0x88], 0x128); + SHA1Final(Hash.data(), &sha); +} + } diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 14599b26..104845d5 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,12 +20,18 @@ #define DSI_NAND_H #include "types.h" +#include "fatfs/ff.h" #include "NDS_Header.h" #include "DSi_TMD.h" +#include "SPI_Firmware.h" +#include +#include #include #include -namespace DSi_NAND +struct AES_ctx; + +namespace melonDS::DSi_NAND { enum @@ -35,28 +41,228 @@ enum TitleData_BannerSav, }; -bool Init(u8* es_keyY); -void DeInit(); +union DSiFirmwareSystemSettings; +union DSiSerialData; +using DSiHardwareInfoN = std::array; +using DSiKey = std::array; -Platform::FileHandle* GetFile(); +class NANDImage +{ +public: + explicit NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept; + explicit NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept; + ~NANDImage(); + NANDImage(const NANDImage&) = delete; + NANDImage& operator=(const NANDImage&) = delete; -void GetIDs(u8* emmc_cid, u64& consoleid); + NANDImage(NANDImage&& other) noexcept; + NANDImage& operator=(NANDImage&& other) noexcept; -void ReadHardwareInfo(u8* dataS, u8* dataN); + Platform::FileHandle* GetFile() { return CurFile; } -void ReadUserData(u8* data); -void PatchUserData(); + [[nodiscard]] const DSiKey& GetEMMCID() const noexcept { return eMMC_CID; } + [[nodiscard]] u64 GetConsoleID() const noexcept { return ConsoleID; } + [[nodiscard]] u64 GetLength() const noexcept { return Length; } -void ListTitles(u32 category, std::vector& titlelist); -bool TitleExists(u32 category, u32 titleid); -void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner); -bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly); -bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly); -void DeleteTitle(u32 category, u32 titleid); + explicit operator bool() const { return CurFile != nullptr; } +private: + friend class NANDMount; + void SetupFATCrypto(AES_ctx* ctx, u32 ctr); + u32 ReadFATBlock(u64 addr, u32 len, u8* buf); + u32 WriteFATBlock(u64 addr, u32 len, const u8* buf); + bool ESEncrypt(u8* data, u32 len) const; + bool ESDecrypt(u8* data, u32 len) const; + Platform::FileHandle* CurFile = nullptr; + DSiKey eMMC_CID; + u64 ConsoleID; + DSiKey FATIV; + DSiKey FATKey; + DSiKey ESKey; + u64 Length; +}; -u32 GetTitleDataMask(u32 category, u32 titleid); -bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); -bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); +class NANDMount +{ +public: + explicit NANDMount(NANDImage& nand) noexcept; + ~NANDMount() noexcept; + NANDMount(const NANDMount&) = delete; + NANDMount& operator=(const NANDMount&) = delete; + + // Move constructor deleted so that the closure passed to FATFS can't be invalidated + NANDMount(NANDMount&&) = delete; + NANDMount& operator=(NANDMount&&) = delete; + + bool ReadSerialData(DSiSerialData& dataS); + bool ReadHardwareInfoN(DSiHardwareInfoN& dataN); + void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); + + bool ReadUserData(DSiFirmwareSystemSettings& data); + + /// Saves the given system settings to the DSi NAND, + /// to both TWLCFG0.dat and TWLCFG1.dat. + bool ApplyUserData(const DSiFirmwareSystemSettings& data); + + void ListTitles(u32 category, std::vector& titlelist); + bool TitleExists(u32 category, u32 titleid); + void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner); + bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly); + bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly); + void DeleteTitle(u32 category, u32 titleid); + + u32 GetTitleDataMask(u32 category, u32 titleid); + bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); + bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); + + bool ImportFile(const char* path, const u8* data, size_t len); + bool ImportFile(const char* path, const char* in); + bool ExportFile(const char* path, const char* out); + void RemoveFile(const char* path); + void RemoveDir(const char* path); + + explicit operator bool() const { return Image != nullptr && CurFS != nullptr; } +private: + u32 GetTitleVersion(u32 category, u32 titleid); + bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version); + bool CreateSaveFile(const char* path, u32 len); + bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly); + UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num); + UINT FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num); + + NANDImage* Image; + + // We keep a pointer to CurFS because fatfs maintains a global pointer to it; + // therefore if we embed the FATFS directly in the object, + // we can't give it move semantics. + std::unique_ptr CurFS; + +}; + + +typedef std::array SHA1Hash; +typedef std::array TitleID; + +/// Firmware settings for the DSi, saved to the NAND as TWLCFG0.dat or TWLCFG1.dat. +/// The DSi mirrors this information to its own firmware for compatibility with NDS games. +/// @note The file is normally 16KiB, but only the first 432 bytes are used; +/// the rest is FF-padded. +/// This struct excludes the padding. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaresystemsettingsdatafiles +union DSiFirmwareSystemSettings +{ + struct + { + SHA1Hash Hash; + u8 Zero00[108]; + u8 Version; + u8 UpdateCounter; + u8 Zero01[2]; + u32 BelowRAMAreaSize; + u32 ConfigFlags; + u8 Zero02; + u8 CountryCode; + Firmware::Language Language; + u8 RTCYear; + u32 RTCOffset; + u8 Zero3[4]; + u8 EULAVersion; + u8 Zero04[9]; + u8 AlarmHour; + u8 AlarmMinute; + u8 Zero05[2]; + bool AlarmEnable; + u8 Zero06[2]; + u8 SystemMenuUsedTitleSlots; + u8 SystemMenuFreeTitleSlots; + u8 Unknown0; + u8 Unknown1; + u8 Zero07[3]; + TitleID SystemMenuMostRecentTitleID; + std::array TouchCalibrationADC1; + std::array TouchCalibrationPixel1; + std::array TouchCalibrationADC2; + std::array TouchCalibrationPixel2; + u8 Unknown2[4]; + u8 Zero08[4]; + u8 FavoriteColor; + u8 Zero09; + u8 BirthdayMonth; + u8 BirthdayDay; + char16_t Nickname[11]; + char16_t Message[27]; + u8 ParentalControlsFlags; + u8 Zero10[6]; + u8 ParentalControlsRegion; + u8 ParentalControlsYearsOfAgeRating; + u8 ParentalControlsSecretQuestion; + u8 Unknown3; + u8 Zero11[2]; + char ParentalControlsPIN[5]; + char16_t ParentalControlsSecretAnswer[65]; + }; + u8 Bytes[432]; + + void UpdateHash(); +}; + +static_assert(sizeof(DSiFirmwareSystemSettings) == 432, "DSiFirmwareSystemSettings must be exactly 432 bytes"); + +enum class ConsoleRegion : u8 +{ + Japan, + USA, + Europe, + Australia, + China, + Korea, +}; + +/// Languages that the given NAND image supports. +/// @see https://problemkaputt.de/gbatek.htm#dsiregions +enum DSiSupportedLanguageMask : u32 { + NoLanguagesSet = 0, + JapaneseSupported = 1 << 0, + EnglishSupported = 1 << 1, + FrenchSupported = 1 << 2, + GermanSupported = 1 << 3, + ItalianSupported = 1 << 4, + SpanishSupported = 1 << 5, + ChineseSupported = 1 << 6, + KoreanSupported = 1 << 7, + + JapanLanguages = JapaneseSupported, + AmericaLanguages = EnglishSupported | FrenchSupported | SpanishSupported, + EuropeLanguages = EnglishSupported | FrenchSupported | GermanSupported | ItalianSupported | SpanishSupported, + AustraliaLanguages = EnglishSupported, + + // "Unknown (supposedly Chinese/Mandarin?, and maybe English or so)" + ChinaLanguages = ChineseSupported | EnglishSupported, + KoreaLanguages = KoreanSupported, +}; + +/// Data file saved to 0:/sys/HWINFO_S.dat. +/// @note The file is normally 16KiB, but only the first 164 bytes are used; +/// the rest is FF-padded. +/// This struct excludes the padding. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaremiscfiles +union DSiSerialData +{ + struct + { + u8 RsaSha1HMAC[0x80]; + u32 Version; + u32 EntrySize; + DSiSupportedLanguageMask SupportedLanguages; + u8 Unknown0[4]; + ConsoleRegion Region; + char Serial[12]; + u8 Unknown1[3]; + u8 TitleIDLSBs[4]; + }; + u8 Bytes[164]; +}; + +static_assert(sizeof(DSiSerialData) == 164, "DSiSerialData must be exactly 164 bytes"); } diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 4db18f3c..7c77c9ad 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,10 +24,12 @@ #include "DSi_AES.h" #include "GPU3D.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -DSi_NDMA::DSi_NDMA(u32 cpu, u32 num) +DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi) : DSi(dsi), CPU(cpu), Num(num) { CPU = cpu; Num = num; @@ -126,7 +128,7 @@ void DSi_NDMA::WriteCnt(u32 val) if ((StartMode & 0x1F) == 0x10) Start(); else if (StartMode == 0x0A) - GPU3D::CheckFIFODMA(); + DSi.GPU.GPU3D.CheckFIFODMA(); // TODO: unsupported start modes: // * timers (00-03) @@ -180,13 +182,13 @@ void DSi_NDMA::Start() //if (SubblockTimer & 0xFFFF) // printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer); - if (NDS::DMAsRunning(CPU)) + if (DSi.DMAsRunning(CPU)) Running = 1; else Running = 2; InProgress = true; - NDS::StopCPU(CPU, 1<<(Num+4)); + DSi.StopCPU(CPU, 1<<(Num+4)); } void DSi_NDMA::Run() @@ -198,7 +200,7 @@ void DSi_NDMA::Run() void DSi_NDMA::Run9() { - if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + if (DSi.ARM9Timestamp >= DSi.ARM9Target) return; Executing = true; @@ -213,11 +215,11 @@ void DSi_NDMA::Run9() if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02) { - unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]; + unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][2] + DSi.ARM9MemTimings[CurDstAddr >> 14][2]; } else { - unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3]; + unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][3] + DSi.ARM9MemTimings[CurDstAddr >> 14][3]; if ((CurSrcAddr >> 24) == (CurDstAddr >> 24)) unitcycles++; else if ((CurSrcAddr >> 24) == 0x02) @@ -233,12 +235,12 @@ void DSi_NDMA::Run9() while (IterCount > 0 && !Stall) { - NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift); + DSi.ARM9Timestamp += (unitcycles << DSi.ARM9ClockShift); if (dofill) - DSi::ARM9Write32(CurDstAddr, FillData); + DSi.ARM9Write32(CurDstAddr, FillData); else - DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr)); + DSi.ARM9Write32(CurDstAddr, DSi.ARM9Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; @@ -246,7 +248,7 @@ void DSi_NDMA::Run9() RemCount--; TotalRemCount--; - if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; + if (DSi.ARM9Timestamp >= DSi.ARM9Target) break; } Executing = false; @@ -257,10 +259,10 @@ void DSi_NDMA::Run9() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(0, 1<<(Num+4)); + DSi.ResumeCPU(0, 1<<(Num+4)); if (StartMode == 0x0A) - GPU3D::CheckFIFODMA(); + DSi.GPU.GPU3D.CheckFIFODMA(); } return; @@ -269,25 +271,25 @@ void DSi_NDMA::Run9() if ((StartMode & 0x1F) == 0x10) // CHECKME { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num); } else if (!(Cnt & (1<<29))) { if (TotalRemCount == 0) { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num); } } Running = 0; InProgress = false; - NDS::ResumeCPU(0, 1<<(Num+4)); + DSi.ResumeCPU(0, 1<<(Num+4)); } void DSi_NDMA::Run7() { - if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + if (DSi.ARM7Timestamp >= DSi.ARM7Target) return; Executing = true; @@ -302,11 +304,11 @@ void DSi_NDMA::Run7() if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02) { - unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]; + unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][2] + DSi.ARM7MemTimings[CurDstAddr >> 15][2]; } else { - unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3]; + unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][3] + DSi.ARM7MemTimings[CurDstAddr >> 15][3]; if ((CurSrcAddr >> 23) == (CurDstAddr >> 23)) unitcycles++; else if ((CurSrcAddr >> 24) == 0x02) @@ -322,12 +324,12 @@ void DSi_NDMA::Run7() while (IterCount > 0 && !Stall) { - NDS::ARM7Timestamp += unitcycles; + DSi.ARM7Timestamp += unitcycles; if (dofill) - DSi::ARM7Write32(CurDstAddr, FillData); + DSi.ARM7Write32(CurDstAddr, FillData); else - DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr)); + DSi.ARM7Write32(CurDstAddr, DSi.ARM7Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; @@ -335,7 +337,7 @@ void DSi_NDMA::Run7() RemCount--; TotalRemCount--; - if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; + if (DSi.ARM7Timestamp >= DSi.ARM7Target) break; } Executing = false; @@ -346,10 +348,10 @@ void DSi_NDMA::Run7() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(1, 1<<(Num+4)); + DSi.ResumeCPU(1, 1<<(Num+4)); - DSi_AES::CheckInputDMA(); - DSi_AES::CheckOutputDMA(); + DSi.AES.CheckInputDMA(); + DSi.AES.CheckOutputDMA(); } return; @@ -358,21 +360,23 @@ void DSi_NDMA::Run7() if ((StartMode & 0x1F) == 0x10) // CHECKME { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num); } else if (!(Cnt & (1<<29))) { if (TotalRemCount == 0) { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num); } } Running = 0; InProgress = false; - NDS::ResumeCPU(1, 1<<(Num+4)); + DSi.ResumeCPU(1, 1<<(Num+4)); - DSi_AES::CheckInputDMA(); - DSi_AES::CheckOutputDMA(); + DSi.AES.CheckInputDMA(); + DSi.AES.CheckOutputDMA(); } + +} \ No newline at end of file diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index b1ea4c96..fb34dbdf 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,10 +22,14 @@ #include "types.h" #include "Savestate.h" +namespace melonDS +{ +class DSi; + class DSi_NDMA { public: - DSi_NDMA(u32 cpu, u32 num); + DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi); ~DSi_NDMA(); void Reset(); @@ -40,12 +44,12 @@ public: void Run9(); void Run7(); - bool IsInMode(u32 mode) + bool IsInMode(u32 mode) const { return ((mode == StartMode) && (Cnt & 0x80000000)); } - bool IsRunning() { return Running!=0; } + bool IsRunning() const { return Running!=0; } void StartIfNeeded(u32 mode) { @@ -73,6 +77,7 @@ public: u32 Cnt; private: + melonDS::DSi& DSi; u32 CPU, Num; u32 StartMode; @@ -94,4 +99,5 @@ private: bool IsGXFIFODMA; }; +} #endif // DSI_NDMA_H diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 04b905a4..a6177dec 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,6 +24,9 @@ #include "WifiAP.h" #include "Platform.h" +namespace melonDS +{ + using Platform::Log; using Platform::LogLevel; @@ -116,38 +119,36 @@ const u8 CIS1[256] = }; -DSi_NWifi* Ctx = nullptr; - - -DSi_NWifi::DSi_NWifi(DSi_SDHost* host) - : DSi_SDDevice(host), - Mailbox - { - // HACK - // the mailboxes are supposed to be 0x80 bytes - // however, as we do things instantly, emulating this is meaningless - // and only adds complication - DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), - DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), - // mailbox 8: extra mailbox acting as a bigger RX buffer - DynamicFIFO(0x8000) - } +DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) : + DSi_SDDevice(host), + Mailbox + { + // HACK + // the mailboxes are supposed to be 0x80 bytes + // however, as we do things instantly, emulating this is meaningless + // and only adds complication + DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), + DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), + // mailbox 8: extra mailbox acting as a bigger RX buffer + DynamicFIFO(0x8000) + }, + DSi(dsi) { + DSi.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer)); + // this seems to control whether the firmware upload is done EEPROMReady = 0; - - Ctx = this; } DSi_NWifi::~DSi_NWifi() { - NDS::CancelEvent(NDS::Event_DSi_NWifi); - Ctx = nullptr; + DSi.CancelEvent(Event_DSi_NWifi); + + DSi.UnregisterEventFunc(Event_DSi_NWifi, 0); } void DSi_NWifi::Reset() { - using namespace SPI_Firmware; TransferCmd = 0xFFFFFFFF; RemSize = 0; @@ -164,26 +165,28 @@ void DSi_NWifi::Reset() for (int i = 0; i < 9; i++) Mailbox[i].Clear(); - MacAddress mac = GetFirmware()->Header().MacAddress; + const Firmware& fw = DSi.SPI.GetFirmware(); + + MacAddress mac = fw.GetHeader().MacAddr; Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - WifiBoard type = GetFirmware()->Header().WifiBoard; + Firmware::WifiBoard type = fw.GetHeader().WifiBoard; switch (type) { - case WifiBoard::W015: // AR6002 + case Firmware::WifiBoard::W015: // AR6002 ROMID = 0x20000188; ChipID = 0x02000001; HostIntAddr = 0x00500400; break; - case WifiBoard::W024: // AR6013 + case Firmware::WifiBoard::W024: // AR6013 ROMID = 0x23000024; ChipID = 0x0D000000; HostIntAddr = 0x00520000; break; - case WifiBoard::W028: // AR6014 (3DS) + case Firmware::WifiBoard::W028: // AR6014 (3DS) ROMID = 0x2300006F; ChipID = 0x0D000001; HostIntAddr = 0x00520000; @@ -224,7 +227,7 @@ void DSi_NWifi::Reset() BeaconTimer = 0x10A2220ULL; ConnectionStatus = 0; - NDS::CancelEvent(NDS::Event_DSi_NWifi); + DSi.CancelEvent(Event_DSi_NWifi); } void DSi_NWifi::DoSavestate(Savestate* file) @@ -895,9 +898,8 @@ void DSi_NWifi::HTC_Command() case 0x0004: // setup complete { - SPI_Firmware::MacAddress mac = SPI_Firmware::GetFirmware()->Header().MacAddress; u8 ready_evt[12]; - memcpy(&ready_evt[0], &mac, mac.size()); + memcpy(&ready_evt[0], &EEPROM[0xA], 6); // MAC address ready_evt[6] = 0x02; ready_evt[7] = 0; *(u32*)&ready_evt[8] = 0x2300006C; @@ -908,7 +910,7 @@ void DSi_NWifi::HTC_Command() SendWMIEvent(1, 0x1006, regdomain_evt, 4); BootPhase = 2; - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0); + DSi.ScheduleEvent(Event_DSi_NWifi, false, 33611, 0, 0); } break; @@ -1549,7 +1551,26 @@ void DSi_NWifi::WindowWrite(u32 addr, u32 val) } -void DSi_NWifi::_MSTimer() +void DSi_NWifi::DrainRXBuffer() +{ + while (Mailbox[8].Level() >= 6) + { + u16 len = Mailbox[8].Peek(2) | (Mailbox[8].Peek(3) << 8); + u32 totallen = len + 6; + u32 required = (totallen + 0x7F) & ~0x7F; + + if (!Mailbox[4].CanFit(required)) + break; + + u32 i = 0; + for (; i < totallen; i++) Mailbox[4].Write(Mailbox[8].Read()); + for (; i < required; i++) Mailbox[4].Write(0); + } + + UpdateIRQ_F1(); +} + +void DSi_NWifi::MSTimer(u32 param) { BeaconTimer++; @@ -1587,29 +1608,8 @@ void DSi_NWifi::_MSTimer() //if (Mailbox[4].IsEmpty()) CheckRX(); } + + DSi.ScheduleEvent(Event_DSi_NWifi, true, 33611, 0, 0); } -void DSi_NWifi::DrainRXBuffer() -{ - while (Mailbox[8].Level() >= 6) - { - u16 len = Mailbox[8].Peek(2) | (Mailbox[8].Peek(3) << 8); - u32 totallen = len + 6; - u32 required = (totallen + 0x7F) & ~0x7F; - - if (!Mailbox[4].CanFit(required)) - break; - - u32 i = 0; - for (; i < totallen; i++) Mailbox[4].Write(Mailbox[8].Read()); - for (; i < required; i++) Mailbox[4].Write(0); - } - - UpdateIRQ_F1(); -} - -void DSi_NWifi::MSTimer(u32 param) -{ - Ctx->_MSTimer(); - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0); -} +} \ No newline at end of file diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index ffd56476..39e9459c 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -23,10 +23,12 @@ #include "FIFO.h" #include "Savestate.h" +namespace melonDS +{ class DSi_NWifi : public DSi_SDDevice { public: - DSi_NWifi(DSi_SDHost* host); + DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host); ~DSi_NWifi(); void Reset(); @@ -40,11 +42,10 @@ public: void SetIRQ_F1_Counter(u32 n); - void _MSTimer(); - - static void MSTimer(u32 param); + void MSTimer(u32 param); private: + melonDS::DSi& DSi; u32 TransferCmd; u32 TransferAddr; u32 RemSize; @@ -148,4 +149,5 @@ private: u8 LANBuffer[2048]; }; +} #endif // DSI_NWIFI_H diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 4f287b17..72fe3756 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -18,11 +18,19 @@ #include #include +#include "Args.h" #include "DSi.h" #include "DSi_SD.h" +#include "DSi_NAND.h" #include "DSi_NWifi.h" #include "Platform.h" +namespace melonDS +{ +using std::holds_alternative; +using std::unique_ptr; +using std::get_if; +using std::get; using namespace Platform; // observed IRQ behavior during transfers @@ -47,27 +55,45 @@ using namespace Platform; #define SD_DESC Num?"SDIO":"SD/MMC" - -DSi_SDHost::DSi_SDHost(u32 num) +enum { - Num = num; + Transfer_TX = 0, + Transfer_RX, +}; - Ports[0] = nullptr; + +DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard) noexcept : DSi(dsi), Num(0) +{ + DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, + Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); + DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, + Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); + + Ports[0] = sdcard ? std::make_unique(DSi, this, std::move(*sdcard)) : nullptr; + sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object + Ports[1] = std::make_unique(DSi, this, std::move(nand)); +} + +// Creates an SDIO host +DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1) +{ + DSi.RegisterEventFunc(Event_DSi_SDIOTransfer , + Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); + DSi.RegisterEventFunc(Event_DSi_SDIOTransfer, + Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); + + Ports[0] = std::make_unique(DSi, this); Ports[1] = nullptr; } DSi_SDHost::~DSi_SDHost() { - if (Ports[0]) delete Ports[0]; - if (Ports[1]) delete Ports[1]; -} + DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, + Transfer_TX); + DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, + Transfer_RX); -void DSi_SDHost::CloseHandles() -{ - if (Ports[0]) delete Ports[0]; - if (Ports[1]) delete Ports[1]; - Ports[0] = nullptr; - Ports[1] = nullptr; + // unique_ptr's destructor will clean up the ports } void DSi_SDHost::Reset() @@ -110,51 +136,70 @@ void DSi_SDHost::Reset() TXReq = false; - CloseHandles(); + if (Ports[0]) Ports[0]->Reset(); + if (Ports[1]) Ports[1]->Reset(); +} - if (Num == 0) +FATStorage* DSi_SDHost::GetSDCard() noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[0].get())->GetSDCard(); +} + +const FATStorage* DSi_SDHost::GetSDCard() const noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[0].get())->GetSDCard(); +} + +DSi_NAND::NANDImage* DSi_SDHost::GetNAND() noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[1].get())->GetNAND(); +} + +const DSi_NAND::NANDImage* DSi_SDHost::GetNAND() const noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[1].get())->GetNAND(); +} + +void DSi_SDHost::SetSDCard(FATStorage&& sdcard) noexcept +{ + if (Num != 0) return; + + static_cast(Ports[0].get())->SetSDCard(std::move(sdcard)); +} + +void DSi_SDHost::SetSDCard(std::optional&& sdcard) noexcept +{ + if (Num != 0) return; + + if (sdcard) { - DSi_MMCStorage* sd; - DSi_MMCStorage* mmc; - - if (Platform::GetConfigBool(Platform::DSiSD_Enable)) + if (!Ports[0]) { - std::string folderpath; - if (Platform::GetConfigBool(Platform::DSiSD_FolderSync)) - folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath); - else - folderpath = ""; - - sd = new DSi_MMCStorage(this, - false, - Platform::GetConfigString(Platform::DSiSD_ImagePath), - (u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024, - Platform::GetConfigBool(Platform::DSiSD_ReadOnly), - folderpath); - u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00}; - sd->SetCID(sd_cid); + Ports[0] = std::make_unique(DSi, this, std::move(*sdcard)); } else - sd = nullptr; - - std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath); - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - mmc = new DSi_MMCStorage(this, true, instnand); - mmc->SetCID(DSi::eMMC_CID); - - Ports[0] = sd; - Ports[1] = mmc; + { + static_cast(Ports[0].get())->SetSDCard(std::move(*sdcard)); + } } else { - DSi_NWifi* nwifi = new DSi_NWifi(this); - - Ports[0] = nwifi; + Ports[0] = nullptr; } - if (Ports[0]) Ports[0]->Reset(); - if (Ports[1]) Ports[1]->Reset(); + sdcard = std::nullopt; + // a moved-from optional isn't empty, it contains a moved-from object +} + +void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept +{ + if (Num != 0) return; + + static_cast(Ports[1].get())->SetNAND(std::move(nand)); } void DSi_SDHost::DoSavestate(Savestate* file) @@ -212,7 +257,7 @@ void DSi_SDHost::UpdateData32IRQ() newflags &= (Data32IRQ >> 11); if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); } void DSi_SDHost::ClearIRQ(u32 irq) @@ -228,7 +273,7 @@ void DSi_SDHost::SetIRQ(u32 irq) u32 newflags = IRQStatus & ~IRQMask; if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); } void DSi_SDHost::UpdateIRQ(u32 oldmask) @@ -237,7 +282,7 @@ void DSi_SDHost::UpdateIRQ(u32 oldmask) u32 newflags = IRQStatus & ~IRQMask; if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); } void DSi_SDHost::SetCardIRQ() @@ -245,7 +290,7 @@ void DSi_SDHost::SetCardIRQ() if (!(CardIRQCtl & (1<<0))) return; u16 oldflags = CardIRQStatus & ~CardIRQMask; - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (dev->IRQ) CardIRQStatus |= (1<<0); else CardIRQStatus &= ~(1<<0); @@ -254,8 +299,8 @@ void DSi_SDHost::SetCardIRQ() if ((oldflags == 0) && (newflags != 0)) // checkme { - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1); } } @@ -266,8 +311,8 @@ void DSi_SDHost::UpdateCardIRQ(u16 oldmask) if ((oldflags == 0) && (newflags != 0)) // checkme { - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1); } } @@ -283,17 +328,15 @@ void DSi_SDHost::SendResponse(u32 val, bool last) void DSi_SDHost::FinishRX(u32 param) { - DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; + CheckSwapFIFO(); - host->CheckSwapFIFO(); - - if (host->DataMode == 1) - host->UpdateFIFO32(); + if (DataMode == 1) + UpdateFIFO32(); else - host->SetIRQ(24); + SetIRQ(24); } -u32 DSi_SDHost::DataRX(u8* data, u32 len) +u32 DSi_SDHost::DataRX(const u8* data, u32 len) { if (len != BlockLen16) { Log(LogLevel::Warn, "!! BAD BLOCKLEN\n"); len = BlockLen16; } @@ -310,21 +353,19 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len) // we need a delay because DSi boot2 will send a command and then wait for IRQ0 // but if IRQ24 is thrown instantly, the handler clears IRQ0 before the // send-command function starts polling IRQ status - u32 param = Num | (last << 1); - NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, - false, 512, FinishRX, param); + DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, + false, 512, Transfer_RX, 0); return len; } void DSi_SDHost::FinishTX(u32 param) { - DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; - DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); - if (host->BlockCountInternal == 0) + if (BlockCountInternal == 0) { - if (host->StopAction & (1<<8)) + if (StopAction & (1<<8)) { if (dev) dev->SendCMD(12, 0); } @@ -332,8 +373,8 @@ void DSi_SDHost::FinishTX(u32 param) // CHECKME: presumably IRQ2 should not trigger here, but rather // when the data transfer is done //SetIRQ(0); - host->SetIRQ(2); - host->TXReq = false; + SetIRQ(2); + TXReq = false; } else { @@ -354,7 +395,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len) if (DataFIFO32.IsEmpty()) { SetIRQ(25); - DSi::CheckNDMAs(1, Num ? 0x29 : 0x28); + DSi.CheckNDMAs(1, Num ? 0x29 : 0x28); } return 0; } @@ -393,13 +434,13 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len) CurFIFO ^= 1; BlockCountInternal--; - NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, - false, 512, FinishTX, Num); + DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, + false, 512, Transfer_TX, 0); return len; } -u32 DSi_SDHost::GetTransferrableLen(u32 len) +u32 DSi_SDHost::GetTransferrableLen(u32 len) const { if (len > BlockLen16) len = BlockLen16; // checkme return len; @@ -407,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len) void DSi_SDHost::CheckRX() { - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); CheckSwapFIFO(); @@ -447,7 +488,7 @@ void DSi_SDHost::CheckTX() return; } - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (dev) dev->ContinueTransfer(); } @@ -524,7 +565,7 @@ u16 DSi_SDHost::Read(u32 addr) case 0x10A: return 0; } - Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1)); + Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, DSi.GetPC(1)); return 0; } @@ -538,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16() return 0; } - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u16 ret = DataFIFO[f].Read(); if (DataFIFO[f].IsEmpty()) @@ -559,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32() return 0; } - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 ret = DataFIFO32.Read(); if (DataFIFO32.IsEmpty()) @@ -581,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val) Command = val; u8 cmd = Command & 0x3F; - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (dev) { // CHECKME @@ -695,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val) void DSi_SDHost::WriteFIFO16(u16 val) { - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 f = CurFIFO; if (DataFIFO[f].IsFull()) { @@ -749,7 +787,7 @@ void DSi_SDHost::UpdateFIFO32() if ((DataFIFO32.Level() << 2) >= BlockLen32) { - DSi::CheckNDMAs(1, Num ? 0x29 : 0x28); + DSi.CheckNDMAs(1, Num ? 0x29 : 0x28); } } @@ -768,41 +806,23 @@ void DSi_SDHost::CheckSwapFIFO() #define MMC_DESC (Internal?"NAND":"SDcard") -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename) - : DSi_SDDevice(host) +DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept + : DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand)) { - Internal = internal; - File = Platform::OpenLocalFile(filename, FileMode::ReadWriteExisting); - - SD = nullptr; - ReadOnly = false; + SetCID(get(Storage).GetEMMCID().data()); } -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir) - : DSi_SDDevice(host) +DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept + : DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard)) { - Internal = internal; - File = nullptr; - - SD = new FATStorage(filename, size, readonly, sourcedir); - SD->Open(); - - ReadOnly = readonly; + ReadOnly = get(Storage).IsReadOnly(); + SetCID(DSiSDCardCID); } -DSi_MMCStorage::~DSi_MMCStorage() -{ - if (SD) - { - SD->Close(); - delete SD; - } - if (File) - { - CloseFile(File); - } -} +// The FATStorage or NANDImage is owned by this object; +// std::variant's destructor will clean it up. +DSi_MMCStorage::~DSi_MMCStorage() = default; void DSi_MMCStorage::Reset() { @@ -831,7 +851,7 @@ void DSi_MMCStorage::Reset() void DSi_MMCStorage::DoSavestate(Savestate* file) { - file->Section(Internal ? "NAND" : "SDCR"); + file->Section(holds_alternative(Storage) ? "NAND" : "SDCR"); file->VarArray(CID, 16); file->VarArray(CSD, 16); @@ -866,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) case 1: // SEND_OP_COND // CHECKME!! // also TODO: it's different for the SD card - if (Internal) + if (std::holds_alternative(Storage)) { param &= ~(1<<30); OCR &= 0xBF000000; @@ -890,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) return; case 3: // get/set RCA - if (Internal) + if (holds_alternative(Storage)) { RCA = param >> 16; Host->SendResponse(CSR|0x10000, true); // huh?? @@ -925,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) case 12: // stop operation SetState(0x04); - if (File) FileFlush(File); + if (auto* nand = get_if(&Storage)) + FileFlush(nand->GetFile()); RWCommand = 0; Host->SendResponse(CSR, true); return; @@ -946,6 +967,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) Host->SendResponse(CSR, true); return; + case 17: // read single block case 18: // read multiple blocks //printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); RWAddress = param; @@ -954,12 +976,14 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) RWAddress <<= 9; BlockSize = 512; } - RWCommand = 18; + if (cmd == 18) + RWCommand = 18; Host->SendResponse(CSR, true); RWAddress += ReadBlock(RWAddress); SetState(0x05); return; + case 24: // write single block case 25: // write multiple blocks //printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); RWAddress = param; @@ -968,7 +992,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) RWAddress <<= 9; BlockSize = 512; } - RWCommand = 25; + if (cmd == 25) + RWCommand = 25; Host->SendResponse(CSR, true); RWAddress += WriteBlock(RWAddress); SetState(0x04); @@ -1002,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param) // DSi boot2 sets this to 0x40100000 (hardcoded) // then has two codepaths depending on whether bit30 did get set // is it settable at all on the MMC? probably not. - if (Internal) param &= ~(1<<30); + if (holds_alternative(Storage)) param &= ~(1<<30); OCR &= 0xBF000000; OCR |= (param & 0x40FFFFFF); Host->SendResponse(OCR, true); @@ -1048,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr) len = Host->GetTransferrableLen(len); u8 data[0x200]; - if (SD) + if (auto* sd = std::get_if(&Storage)) { - SD->ReadSectors((u32)(addr >> 9), 1, data); + sd->ReadSectors((u32)(addr >> 9), 1, data); } - else if (File) + else if (auto* nand = std::get_if(&Storage)) { - FileSeek(File, addr, FileSeekOrigin::Start); - FileRead(&data[addr & 0x1FF], 1, len, File); + FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start); + FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile()); } return Host->DataRX(&data[addr & 0x1FF], len); @@ -1069,26 +1094,28 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr) u8 data[0x200]; if (len < 0x200) { - if (SD) + if (auto* sd = get_if(&Storage)) { - SD->ReadSectors((u32)(addr >> 9), 1, data); + sd->ReadSectors((u32)(addr >> 9), 1, data); } } if ((len = Host->DataTX(&data[addr & 0x1FF], len))) { if (!ReadOnly) { - if (SD) + if (auto* sd = get_if(&Storage)) { - SD->WriteSectors((u32)(addr >> 9), 1, data); + sd->WriteSectors((u32)(addr >> 9), 1, data); } - else if (File) + else if (auto* nand = get_if(&Storage)) { - FileSeek(File, addr, FileSeekOrigin::Start); - FileWrite(&data[addr & 0x1FF], 1, len, File); + FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start); + FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile()); } } } return len; } + +} diff --git a/src/DSi_SD.h b/src/DSi_SD.h index fe9e23ab..29620dc5 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,30 +20,40 @@ #define DSI_SD_H #include +#include #include "FIFO.h" #include "FATStorage.h" +#include "DSi_NAND.h" #include "Savestate.h" +namespace melonDS +{ class DSi_SDDevice; +class DSi; +using Nothing = std::monostate; +using DSiStorage = std::variant; class DSi_SDHost { public: - DSi_SDHost(u32 num); + /// Creates an SDMMC host. + DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard = std::nullopt) noexcept; + + /// Creates an SDIO host + explicit DSi_SDHost(melonDS::DSi& dsi) noexcept; ~DSi_SDHost(); - void CloseHandles(); void Reset(); void DoSavestate(Savestate* file); - static void FinishRX(u32 param); - static void FinishTX(u32 param); + void FinishRX(u32 param); + void FinishTX(u32 param); void SendResponse(u32 val, bool last); - u32 DataRX(u8* data, u32 len); + u32 DataRX(const u8* data, u32 len); u32 DataTX(u8* data, u32 len); - u32 GetTransferrableLen(u32 len); + u32 GetTransferrableLen(u32 len) const; void CheckRX(); void CheckTX(); @@ -51,6 +61,15 @@ public: void SetCardIRQ(); + [[nodiscard]] FATStorage* GetSDCard() noexcept; + [[nodiscard]] const FATStorage* GetSDCard() const noexcept; + [[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept; + [[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept; + + void SetSDCard(FATStorage&& sdcard) noexcept; + void SetSDCard(std::optional&& sdcard) noexcept; + void SetNAND(DSi_NAND::NANDImage&& nand) noexcept; + u16 Read(u32 addr); void Write(u32 addr, u16 val); u16 ReadFIFO16(); @@ -62,6 +81,7 @@ public: void CheckSwapFIFO(); private: + melonDS::DSi& DSi; u32 Num; u16 PortSelect; @@ -87,7 +107,7 @@ private: u32 Param; u16 ResponseBuffer[8]; - DSi_SDDevice* Ports[2]; + std::array, 2> Ports {}; u32 CurFIFO; // FIFO accessible for read/write FIFO DataFIFO[2]; @@ -125,25 +145,53 @@ protected: class DSi_MMCStorage : public DSi_SDDevice { public: - DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename); - DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); - ~DSi_MMCStorage(); + DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept; + DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept; + ~DSi_MMCStorage() override; - void Reset(); + [[nodiscard]] FATStorage* GetSDCard() noexcept { return std::get_if(&Storage); } + [[nodiscard]] const FATStorage* GetSDCard() const noexcept { return std::get_if(&Storage); } + [[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept { return std::get_if(&Storage); } + [[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept { return std::get_if(&Storage); } - void DoSavestate(Savestate* file); + void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { Storage = std::move(nand); } + void SetSDCard(FATStorage&& sdcard) noexcept { Storage = std::move(sdcard); } + void SetSDCard(std::optional&& sdcard) noexcept + { + if (sdcard) + { // If we're setting a new SD card... + Storage = std::move(*sdcard); + sdcard = std::nullopt; + } + else + { + Storage = Nothing(); + } + } - void SetCID(u8* cid) { memcpy(CID, cid, 16); } + void SetStorage(DSiStorage&& storage) noexcept + { + Storage = std::move(storage); + storage = Nothing(); + // not sure if a moved-from variant is empty or contains a moved-from object; + // better to be safe than sorry + } - void SendCMD(u8 cmd, u32 param); + void Reset() override; + + void DoSavestate(Savestate* file) override; + + void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); } + + void SendCMD(u8 cmd, u32 param) override; void SendACMD(u8 cmd, u32 param); - void ContinueTransfer(); + void ContinueTransfer() override; private: - bool Internal; - Platform::FileHandle* File; - FATStorage* SD; + static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00}; + melonDS::DSi& DSi; + DSiStorage Storage; u8 CID[16]; u8 CSD[16]; @@ -164,4 +212,5 @@ private: u32 WriteBlock(u64 addr); }; +} #endif // DSI_SD_H diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index c690f9c9..d515db9f 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,38 +19,27 @@ #include #include #include "DSi.h" -#include "SPI.h" #include "DSi_SPI_TSC.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace DSi_SPI_TSC -{ -u32 DataPos; -u8 Index; -u8 Bank; -u8 Data; - -u8 Bank3Regs[0x80]; -u8 TSCMode; - -u16 TouchX, TouchY; - - -bool Init() -{ - return true; -} - -void DeInit() +DSi_TSC::DSi_TSC(melonDS::DSi& dsi) : TSC(dsi) { } -void Reset() +DSi_TSC::~DSi_TSC() { +} + +void DSi_TSC::Reset() +{ + TSC::Reset(); + DataPos = 0; Bank = 0; @@ -72,8 +61,10 @@ void Reset() TSCMode = 0x01; // DSi mode } -void DoSavestate(Savestate* file) +void DSi_TSC::DoSavestate(Savestate* file) { + TSC::DoSavestate(file); + file->Section("SPTi"); file->Var32(&DataPos); @@ -85,19 +76,14 @@ void DoSavestate(Savestate* file) file->Var8(&TSCMode); } -void SetMode(u8 mode) +void DSi_TSC::SetMode(u8 mode) { TSCMode = mode; } -void SetTouchCoords(u16 x, u16 y) +void DSi_TSC::SetTouchCoords(u16 x, u16 y) { - if (TSCMode == 0x00) - { - if (y == 0xFFF) NDS::KeyInput |= (1 << (16+6)); - else NDS::KeyInput &= ~(1 << (16+6)); - return SPI_TSC::SetTouchCoords(x, y); - } + if (TSCMode == 0x00) return TSC::SetTouchCoords(x, y); TouchX = x; TouchY = y; @@ -135,24 +121,17 @@ void SetTouchCoords(u16 x, u16 y) } } -void MicInputFrame(s16* data, int samples) +void DSi_TSC::MicInputFrame(const s16* data, int samples) { - if (TSCMode == 0x00) return SPI_TSC::MicInputFrame(data, samples); + if (TSCMode == 0x00) return TSC::MicInputFrame(data, samples); // otherwise we don't handle mic input // TODO: handle it where it needs to be } -u8 Read() +void DSi_TSC::Write(u8 val) { - if (TSCMode == 0x00) return SPI_TSC::Read(); - - return Data; -} - -void Write(u8 val, u32 hold) -{ - if (TSCMode == 0x00) return SPI_TSC::Write(val, hold); + if (TSCMode == 0x00) return TSC::Write(val); #define READWRITE(var) { if (Index & 0x01) Data = var; else var = val; } @@ -219,7 +198,7 @@ void Write(u8 val, u32 hold) { Log(LogLevel::Debug, "DSi_SPI_TSC: DS-compatibility mode\n"); DataPos = 0; - NDS::KeyInput |= (1 << (16+6)); + NDS.KeyInput |= (1 << (16+6)); return; } } @@ -233,8 +212,14 @@ void Write(u8 val, u32 hold) Index += (1<<1); // increment index } - if (hold) DataPos++; - else DataPos = 0; + DataPos++; } +void DSi_TSC::Release() +{ + if (TSCMode == 0x00) return TSC::Release(); + + DataPos = 0; } + +} \ No newline at end of file diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index 7a3acf43..d1a71063 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,26 +21,37 @@ #include "types.h" #include "Savestate.h" +#include "SPI.h" -namespace DSi_SPI_TSC +namespace melonDS { +class DSi; +class DSi_TSC : public TSC +{ +public: + DSi_TSC(melonDS::DSi& dsi); + ~DSi_TSC() override; -extern u32 DataPos; + void Reset() override; -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; -// 00=DS-mode 01=normal -void SetMode(u8 mode); + // 00=DS-mode 01=normal + void SetMode(u8 mode); -void SetTouchCoords(u16 x, u16 y); -void MicInputFrame(s16* data, int samples); + void SetTouchCoords(u16 x, u16 y) override; + void MicInputFrame(const s16* data, int samples) override; -u8 Read(); -void Write(u8 val, u32 hold); + void Write(u8 val) override; + void Release() override; + +private: + u8 Index; + u8 Bank; + + u8 Bank3Regs[0x80]; + u8 TSCMode; +}; } - #endif // DSI_SPI_TSC diff --git a/src/DSi_TMD.h b/src/DSi_TMD.h index 12226c55..f07b3d1c 100644 --- a/src/DSi_TMD.h +++ b/src/DSi_TMD.h @@ -22,7 +22,7 @@ #include "types.h" #include -namespace DSi_TMD +namespace melonDS::DSi_TMD { struct TitleMetadataContent { diff --git a/src/fatfs/diskio.c b/src/FATIO.cpp similarity index 77% rename from src/fatfs/diskio.c rename to src/FATIO.cpp index 5b5c0545..233014e4 100644 --- a/src/fatfs/diskio.c +++ b/src/FATIO.cpp @@ -1,15 +1,26 @@ -/*-----------------------------------------------------------------------*/ -/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ -/*-----------------------------------------------------------------------*/ -/* If a working storage control module is available, it should be */ -/* attached to the FatFs via a glue function rather than modifying it. */ -/* This is an example of glue functions to attach various exsisting */ -/* storage control modules to the FatFs module with a defined API. */ -/*-----------------------------------------------------------------------*/ +/* + Copyright 2016-2023 melonDS team -#include "ff.h" /* Obtains integer types */ -#include "diskio.h" /* Declarations of disk functions */ + This file is part of melonDS. + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "FATIO.h" +#include "fatfs/ff.h" +#include "fatfs/diskio.h" + +using namespace melonDS; static ff_disk_read_cb ReadCb; static ff_disk_write_cb WriteCb; @@ -17,7 +28,7 @@ static LBA_t SectorCount; static DSTATUS Status = STA_NOINIT | STA_NODISK; -void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt) +void melonDS::ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt) { if (!readcb) return; @@ -30,10 +41,10 @@ void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt else Status &= ~STA_PROTECT; } -void ff_disk_close(void) +void melonDS::ff_disk_close() { - ReadCb = (void*)0; - WriteCb = (void*)0; + ReadCb = nullptr; + WriteCb = nullptr; SectorCount = 0; Status &= ~STA_PROTECT; diff --git a/src/FATIO.h b/src/FATIO.h new file mode 100644 index 00000000..6d8aa499 --- /dev/null +++ b/src/FATIO.h @@ -0,0 +1,36 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + + +#ifndef FATIO_H +#define FATIO_H + +#include +#include "fatfs/ff.h" + +// extra additions for interfacing with melonDS +namespace melonDS +{ +using ff_disk_read_cb = std::function; +using ff_disk_write_cb = std::function; + +void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt); +void ff_disk_close(); +} + +#endif // FATIO_H diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index d882f8dc..f735d4b8 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,52 +21,88 @@ #include #include +#include "FATIO.h" #include "FATStorage.h" #include "Platform.h" +namespace melonDS +{ namespace fs = std::filesystem; using namespace Platform; +using std::string; -FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir) +FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir) : + FATStorage(FATStorageArgs { filename, size, readonly, sourcedir }) { - ReadOnly = readonly; - Load(filename, size, sourcedir); +} - File = nullptr; +FATStorage::FATStorage(const FATStorageArgs& args) noexcept : + FATStorage(args.Filename, args.Size, args.ReadOnly, args.SourceDir) +{ +} + +FATStorage::FATStorage(FATStorageArgs&& args) noexcept : + FilePath(std::move(args.Filename)), + FileSize(args.Size), + ReadOnly(args.ReadOnly), + SourceDir(std::move(args.SourceDir)) +{ + Load(FilePath, FileSize, SourceDir); +} + +FATStorage::FATStorage(FATStorage&& other) noexcept +{ + FilePath = std::move(other.FilePath); + IndexPath = std::move(other.IndexPath); + SourceDir = std::move(other.SourceDir); + ReadOnly = other.ReadOnly; + File = other.File; + FileSize = other.FileSize; + DirIndex = std::move(other.DirIndex); + FileIndex = std::move(other.FileIndex); + + other.File = nullptr; +} + +FATStorage& FATStorage::operator=(FATStorage&& other) noexcept +{ + if (this != &other) + { + if (File) + { // Sync this file's contents to the host (if applicable) before closing it + if (!ReadOnly) Save(); + CloseFile(File); + } + + FilePath = std::move(other.FilePath); + IndexPath = std::move(other.IndexPath); + SourceDir = std::move(other.SourceDir); + ReadOnly = other.ReadOnly; + File = other.File; + FileSize = other.FileSize; + DirIndex = std::move(other.DirIndex); + FileIndex = std::move(other.FileIndex); + + other.File = nullptr; + other.SourceDir = std::nullopt; + } + + return *this; } FATStorage::~FATStorage() { if (!ReadOnly) Save(); -} - -bool FATStorage::Open() -{ - File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); - if (!File) - { - return false; - } - - return true; -} - -void FATStorage::Close() -{ if (File) CloseFile(File); File = nullptr; } - bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) { if (!File) return false; - if (FF_File) return false; - FF_File = File; - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); FRESULT res; FATFS fs; @@ -74,8 +110,8 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) res = f_mount(&fs, "0:", 1); if (res != FR_OK) { + f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return false; } @@ -87,7 +123,6 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) { f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return false; } @@ -97,34 +132,75 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return nwrite==len; } +u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) +{ + if (!File) return false; -u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); + + FRESULT res; + FATFS fs; + + res = f_mount(&fs, "0:", 1); + if (res != FR_OK) + { + f_unmount("0:"); + ff_disk_close(); + return false; + } + + std::string prefixedPath("0:/"); + prefixedPath += path; + FF_FIL file; + res = f_open(&file, prefixedPath.c_str(), FA_READ); + if (res != FR_OK) + { + f_unmount("0:"); + ff_disk_close(); + return false; + } + + u32 nread; + f_lseek(&file, start); + f_read(&file, data, len, &nread); + f_close(&file); + + f_unmount("0:"); + ff_disk_close(); + return nread; +} + +u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) const { return ReadSectorsInternal(File, FileSize, start, num, data); } -u32 FATStorage::WriteSectors(u32 start, u32 num, u8* data) +u32 FATStorage::WriteSectors(u32 start, u32 num, const u8* data) { if (ReadOnly) return 0; return WriteSectorsInternal(File, FileSize, start, num, data); } - -FileHandle* FATStorage::FF_File; -u64 FATStorage::FF_FileSize; - -UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num) +u64 FATStorage::GetSectorCount() const { - return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf); + return FileSize / 0x200; } -UINT FATStorage::FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num) +ff_disk_read_cb FATStorage::FF_ReadStorage() const noexcept { - return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf); + return [this](BYTE* buf, LBA_t sector, UINT num) { + return ReadSectorsInternal(File, FileSize, sector, num, buf); + }; +} + +ff_disk_write_cb FATStorage::FF_WriteStorage() const noexcept +{ + return [this](const BYTE* buf, LBA_t sector, UINT num) { + return WriteSectorsInternal(File, FileSize, sector, num, buf); + }; } @@ -157,7 +233,7 @@ u32 FATStorage::ReadSectorsInternal(FileHandle* file, u64 filelen, u32 start, u3 return res; } -u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, u8* data) +u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data) { if (!file) return 0; @@ -318,17 +394,17 @@ void FATStorage::SaveIndex() FileHandle* f = OpenLocalFile(IndexPath, FileMode::WriteText); if (!f) return; - FileWriteFormatted(f, "SIZE %" PRIu64 "\r\n", FileSize); + FileWriteFormatted(f, "SIZE %" PRIu64 "\n", FileSize); for (const auto& [key, val] : DirIndex) { - FileWriteFormatted(f, "DIR %u %s\r\n", + FileWriteFormatted(f, "DIR %u %s\n", val.IsReadOnly?1:0, val.Path.c_str()); } for (const auto& [key, val] : FileIndex) { - FileWriteFormatted(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\r\n", + FileWriteFormatted(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\n", val.IsReadOnly?1:0, val.Size, val.LastModified, val.LastModifiedInternal, val.Path.c_str()); } @@ -904,7 +980,7 @@ bool FATStorage::ImportDirectory(const std::string& sourcedir) return true; } -u64 FATStorage::GetDirectorySize(fs::path sourcedir) +u64 FATStorage::GetDirectorySize(fs::path sourcedir) const { u64 ret = 0; u32 csize = 0x1000; // this is an estimate @@ -927,19 +1003,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir) return ret; } -bool FATStorage::Load(const std::string& filename, u64 size, const std::string& sourcedir) +bool FATStorage::Load(const std::string& filename, u64 size, const std::optional& sourcedir) { - FilePath = filename; - FileSize = size; - SourceDir = sourcedir; - - bool hasdir = !sourcedir.empty(); - if (hasdir) + bool hasdir = sourcedir && !sourcedir->empty(); + if (sourcedir) { - if (!fs::is_directory(fs::u8path(sourcedir))) + if (!fs::is_directory(fs::u8path(*sourcedir))) { hasdir = false; - SourceDir = ""; + SourceDir = std::nullopt; } } @@ -950,8 +1022,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& // with a minimum 128MB extra, otherwise size is defaulted to 512MB bool isnew = !Platform::LocalFileExists(filename); - FF_File = Platform::OpenLocalFile(filename, static_cast(FileMode::ReadWrite | FileMode::Preserve)); - if (!FF_File) + File = Platform::OpenLocalFile(filename, static_cast(FileMode::ReadWrite | FileMode::Preserve)); + if (!File) return false; IndexPath = FilePath + ".idx"; @@ -967,7 +1039,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& if (FileSize == 0) { - FileSize = FileLength(FF_File); + FileSize = FileLength(File); } } @@ -981,8 +1053,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& } else { - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); res = f_mount(&fs, "0:", 1); if (res != FR_OK) @@ -1002,7 +1073,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& { if (hasdir) { - FileSize = GetDirectorySize(fs::u8path(sourcedir)); + FileSize = GetDirectorySize(fs::u8path(*sourcedir)); FileSize += 0x8000000ULL; // 128MB leeway // make it a power of two @@ -1018,9 +1089,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& FileSize = 0x20000000ULL; // 512MB } - FF_FileSize = FileSize; ff_disk_close(); - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); DirIndex.clear(); FileIndex.clear(); @@ -1051,33 +1121,24 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& if (res == FR_OK) { if (hasdir) - ImportDirectory(sourcedir); + ImportDirectory(*sourcedir); } f_unmount("0:"); ff_disk_close(); - CloseFile(FF_File); - FF_File = nullptr; return true; } bool FATStorage::Save() { - if (SourceDir.empty()) - { - return true; + if (!SourceDir) + { // If we're not syncing the SD card image to a host directory... + return true; // Not an error. } - FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); - if (!FF_File) - { - return false; - } - - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); FRESULT res; FATFS fs; @@ -1085,21 +1146,20 @@ bool FATStorage::Save() res = f_mount(&fs, "0:", 1); if (res != FR_OK) { + f_unmount("0:"); ff_disk_close(); - CloseFile(FF_File); - FF_File = nullptr; return false; } - ExportChanges(SourceDir); + ExportChanges(*SourceDir); SaveIndex(); f_unmount("0:"); ff_disk_close(); - CloseFile(FF_File); - FF_File = nullptr; return true; } + +} \ No newline at end of file diff --git a/src/FATStorage.h b/src/FATStorage.h index 6b9beb50..00628461 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,43 +22,66 @@ #include #include #include +#include #include #include "Platform.h" #include "types.h" #include "fatfs/ff.h" +#include "FATIO.h" +namespace melonDS +{ +/// Contains information necessary to load an SD card image. +/// The intended use case is for loading homebrew NDS ROMs; +/// you won't know that a ROM is homebrew until you parse it, +/// so if you load the SD card before the ROM +/// then you might end up discarding it. +struct FATStorageArgs +{ + std::string Filename; + + /// Size of the desired SD card in bytes, or 0 for auto-detect. + u64 Size; + bool ReadOnly; + std::optional SourceDir; +}; class FATStorage { public: - FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); + FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir = std::nullopt); + explicit FATStorage(const FATStorageArgs& args) noexcept; + explicit FATStorage(FATStorageArgs&& args) noexcept; + FATStorage(FATStorage&& other) noexcept; + FATStorage(const FATStorage& other) = delete; + FATStorage& operator=(const FATStorage& other) = delete; + FATStorage& operator=(FATStorage&& other) noexcept; ~FATStorage(); - bool Open(); - void Close(); - bool InjectFile(const std::string& path, u8* data, u32 len); + u32 ReadFile(const std::string& path, u32 start, u32 len, u8* data); - u32 ReadSectors(u32 start, u32 num, u8* data); - u32 WriteSectors(u32 start, u32 num, u8* data); + u32 ReadSectors(u32 start, u32 num, u8* data) const; + u32 WriteSectors(u32 start, u32 num, const u8* data); + + [[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; } + u64 GetSectorCount() const; private: std::string FilePath; std::string IndexPath; - std::string SourceDir; + std::optional SourceDir; bool ReadOnly; Platform::FileHandle* File; u64 FileSize; - static Platform::FileHandle* FF_File; - static u64 FF_FileSize; - static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num); - static UINT FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num); + [[nodiscard]] ff_disk_read_cb FF_ReadStorage() const noexcept; + [[nodiscard]] ff_disk_write_cb FF_WriteStorage() const noexcept; static u32 ReadSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); - static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); + static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data); void LoadIndex(); void SaveIndex(); @@ -73,9 +96,9 @@ private: void CleanupDirectory(const std::string& sourcedir, const std::string& path, int level); bool ImportFile(const std::string& path, std::filesystem::path in); bool ImportDirectory(const std::string& sourcedir); - u64 GetDirectorySize(std::filesystem::path sourcedir); + u64 GetDirectorySize(std::filesystem::path sourcedir) const; - bool Load(const std::string& filename, u64 size, const std::string& sourcedir); + bool Load(const std::string& filename, u64 size, const std::optional& sourcedir); bool Save(); typedef struct @@ -99,4 +122,5 @@ private: std::map FileIndex; }; +} #endif // FATSTORAGE_H diff --git a/src/FIFO.h b/src/FIFO.h index 80d9b8a0..026c2c7f 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,6 +22,8 @@ #include "types.h" #include "Savestate.h" +namespace melonDS +{ template class FIFO { @@ -72,12 +74,12 @@ public: return ret; } - T Peek() + T Peek() const { return Entries[ReadPos]; } - T Peek(u32 offset) + T Peek(u32 offset) const { u32 pos = ReadPos + offset; if (pos >= NumEntries) @@ -86,11 +88,11 @@ public: return Entries[pos]; } - u32 Level() { return NumOccupied; } - bool IsEmpty() { return NumOccupied == 0; } - bool IsFull() { return NumOccupied >= NumEntries; } + u32 Level() const { return NumOccupied; } + bool IsEmpty() const { return NumOccupied == 0; } + bool IsFull() const { return NumOccupied >= NumEntries; } - bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); } + bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); } private: T Entries[NumEntries] = {0}; @@ -162,12 +164,12 @@ public: return ret; } - T Peek() + T Peek() const { return Entries[ReadPos]; } - T Peek(u32 offset) + T Peek(u32 offset) const { u32 pos = ReadPos + offset; if (pos >= NumEntries) @@ -176,11 +178,11 @@ public: return Entries[pos]; } - u32 Level() { return NumOccupied; } - bool IsEmpty() { return NumOccupied == 0; } - bool IsFull() { return NumOccupied >= NumEntries; } + u32 Level() const { return NumOccupied; } + bool IsEmpty() const { return NumOccupied == 0; } + bool IsFull() const { return NumOccupied >= NumEntries; } - bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); } + bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); } private: u32 NumEntries; @@ -189,4 +191,5 @@ private: u32 ReadPos, WritePos; }; +} #endif diff --git a/src/FreeBIOS.cpp b/src/FreeBIOS.cpp index 972f4b8e..f5d8f366 100644 --- a/src/FreeBIOS.cpp +++ b/src/FreeBIOS.cpp @@ -26,10 +26,12 @@ #include "FreeBIOS.h" -unsigned char bios_arm7_bin[] = { +namespace melonDS +{ +std::array bios_arm7_bin = { 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea, - 0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, + 0xe5, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -383,17 +385,17 @@ unsigned char bios_arm7_bin[] = { 0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5, 0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3, 0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1, - 0xd4, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, + 0xdc, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x5c, 0x11, 0x00, 0x00, 0x90, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x4c, 0x11, 0x00, 0x00, 0xbc, 0x11, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x00, 0xe8, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x44, 0x12, 0x00, 0x00, 0xc4, 0x12, 0x00, 0x00, 0x04, 0x13, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00, - 0xe8, 0x13, 0x00, 0x00, 0xf0, 0x13, 0x00, 0x00, 0x84, 0x14, 0x00, 0x00, - 0x00, 0x15, 0x00, 0x00, 0xac, 0x15, 0x00, 0x00, 0xb0, 0x15, 0x00, 0x00, - 0xb0, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, - 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x88, 0x16, 0x00, 0x00, - 0x9c, 0x1c, 0x00, 0x00, 0xc4, 0x1f, 0x00, 0x00, 0x84, 0x1f, 0x00, 0x00, - 0xa0, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, + 0xf0, 0x13, 0x00, 0x00, 0xf8, 0x13, 0x00, 0x00, 0x8c, 0x14, 0x00, 0x00, + 0x08, 0x15, 0x00, 0x00, 0xb4, 0x15, 0x00, 0x00, 0xb8, 0x15, 0x00, 0x00, + 0xb8, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, + 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x90, 0x16, 0x00, 0x00, + 0xa4, 0x1c, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x8c, 0x1f, 0x00, 0x00, + 0xa8, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, 0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1, 0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x20, 0xa0, 0xe3, 0x01, 0x23, 0xc0, 0xe5, @@ -440,205 +442,206 @@ unsigned char bios_arm7_bin[] = { 0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c, 0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44, 0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2, - 0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, + 0xff, 0x04, 0xc0, 0xe3, 0xff, 0x08, 0xc0, 0xe3, 0xa2, 0x20, 0xb0, 0xe1, + 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, 0x80, 0x50, 0x04, 0xe0, + 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, + 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, + 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, - 0x20, 0x00, 0xbd, 0xe8, 0x50, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, - 0x4e, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, - 0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, - 0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, - 0x15, 0x5c, 0xa0, 0xe1, 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, - 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, - 0x83, 0x31, 0xa0, 0x01, 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, - 0x04, 0x00, 0x5c, 0xe3, 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, - 0x01, 0x60, 0xd0, 0x04, 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, - 0x36, 0x6c, 0xa0, 0xe1, 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, - 0x04, 0x90, 0x89, 0x10, 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, - 0x20, 0x00, 0x58, 0xe3, 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, - 0x00, 0x80, 0xa0, 0x03, 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, - 0xe0, 0x03, 0xbd, 0xe8, 0x29, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xb0, 0xe1, 0x26, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a, - 0x01, 0xe0, 0x5e, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, - 0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x0d, 0xff, 0xff, 0x0a, - 0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, - 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x07, 0xff, 0xff, 0x0a, - 0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, - 0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, - 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, - 0x20, 0x00, 0xbd, 0x08, 0xf0, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, - 0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, - 0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, - 0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, + 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, 0x20, 0x00, 0xbd, 0xe8, + 0x4e, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, 0x4c, 0xff, 0xff, 0xea, + 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x02, 0xc0, 0xd2, 0xe5, + 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, 0xa4, 0x2f, 0xa0, 0xe1, + 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, 0x15, 0x5c, 0xa0, 0xe1, + 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, + 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, 0x83, 0x31, 0xa0, 0x01, + 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, 0x04, 0x00, 0x5c, 0xe3, + 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, 0x01, 0x60, 0xd0, 0x04, + 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, 0x36, 0x6c, 0xa0, 0xe1, + 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, 0x04, 0x90, 0x89, 0x10, + 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, 0x20, 0x00, 0x58, 0xe3, + 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, 0x00, 0x80, 0xa0, 0x03, + 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, 0xe0, 0x03, 0xbd, 0xe8, + 0x27, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, + 0x24, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x14, 0xff, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xed, 0xff, 0xff, 0x5a, + 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x0b, 0xff, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, + 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, + 0x23, 0x24, 0xb0, 0xe1, 0x05, 0xff, 0xff, 0x0a, 0x20, 0x00, 0x2d, 0xe9, + 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xdc, 0x04, + 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, - 0xe2, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, - 0xd9, 0xff, 0xff, 0xea, 0xde, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, - 0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, - 0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, - 0xd3, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, - 0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xcb, 0xfe, 0xff, 0x0a, - 0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, - 0x00, 0x00, 0x24, 0x03, 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f, - 0xc8, 0x12, 0xe2, 0x15, 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22, - 0x28, 0x25, 0x26, 0x28, 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33, - 0xba, 0x36, 0x8c, 0x39, 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44, - 0x1c, 0x47, 0xb4, 0x49, 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53, - 0xf5, 0x55, 0x42, 0x58, 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60, - 0xf1, 0x62, 0xe8, 0x64, 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c, - 0xc9, 0x6d, 0x5e, 0x6f, 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75, - 0x41, 0x76, 0x6b, 0x77, 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b, - 0x29, 0x7c, 0xe3, 0x7c, 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f, - 0x61, 0x7f, 0xa6, 0x7f, 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0, - 0x8c, 0x10, 0x4f, 0xe2, 0xb0, 0x00, 0x91, 0xe1, 0xa4, 0xfe, 0xff, 0xea, - 0x00, 0x00, 0x3b, 0x00, 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01, - 0x64, 0x01, 0x9f, 0x01, 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02, - 0xca, 0x02, 0x05, 0x03, 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03, - 0x31, 0x04, 0x6e, 0x04, 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05, - 0x9b, 0x05, 0xd8, 0x05, 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06, - 0x07, 0x07, 0x43, 0x07, 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08, - 0x74, 0x08, 0xb1, 0x08, 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09, - 0xe4, 0x09, 0x21, 0x0a, 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b, - 0x56, 0x0b, 0x93, 0x0b, 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c, - 0xc9, 0x0c, 0x07, 0x0d, 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e, - 0x3f, 0x0e, 0x7d, 0x0e, 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f, - 0xb6, 0x0f, 0xf5, 0x0f, 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10, - 0x30, 0x11, 0x6f, 0x11, 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12, - 0xac, 0x12, 0xeb, 0x12, 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13, - 0x2a, 0x14, 0x6a, 0x14, 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15, - 0xaa, 0x15, 0xea, 0x15, 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16, - 0x2c, 0x17, 0x6c, 0x17, 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18, - 0xb0, 0x18, 0xf0, 0x18, 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19, - 0x36, 0x1a, 0x77, 0x1a, 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b, - 0xbe, 0x1b, 0x00, 0x1c, 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d, - 0x48, 0x1d, 0x8a, 0x1d, 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e, - 0xd5, 0x1e, 0x17, 0x1f, 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20, - 0x64, 0x20, 0xa6, 0x20, 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21, - 0xf5, 0x21, 0x38, 0x22, 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23, - 0x88, 0x23, 0xcb, 0x23, 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24, - 0x1d, 0x25, 0x61, 0x25, 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26, - 0xb4, 0x26, 0xf8, 0x26, 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28, - 0x4e, 0x28, 0x92, 0x28, 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29, - 0xea, 0x29, 0x2f, 0x2a, 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b, - 0x88, 0x2b, 0xcd, 0x2b, 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c, - 0x28, 0x2d, 0x6e, 0x2d, 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e, - 0xcb, 0x2e, 0x11, 0x2f, 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30, - 0x70, 0x30, 0xb6, 0x30, 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31, - 0x17, 0x32, 0x5e, 0x32, 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33, - 0xc1, 0x33, 0x08, 0x34, 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35, - 0x6c, 0x35, 0xb4, 0x35, 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36, - 0x1a, 0x37, 0x62, 0x37, 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38, - 0xcb, 0x38, 0x13, 0x39, 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a, - 0x7e, 0x3a, 0xc6, 0x3a, 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b, - 0x33, 0x3c, 0x7c, 0x3c, 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d, - 0xea, 0x3d, 0x34, 0x3e, 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f, - 0xa4, 0x3f, 0xee, 0x3f, 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41, - 0x61, 0x41, 0xab, 0x41, 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42, - 0x1f, 0x43, 0x6a, 0x43, 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44, - 0xe1, 0x44, 0x2c, 0x45, 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46, - 0xa4, 0x46, 0xf0, 0x46, 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48, - 0x6a, 0x48, 0xb6, 0x48, 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49, - 0x33, 0x4a, 0x7f, 0x4a, 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b, - 0xfe, 0x4b, 0x4a, 0x4c, 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d, - 0xcb, 0x4d, 0x18, 0x4e, 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f, - 0x9b, 0x4f, 0xe9, 0x4f, 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51, - 0x6e, 0x51, 0xbc, 0x51, 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52, - 0x43, 0x53, 0x91, 0x53, 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54, - 0x1a, 0x55, 0x69, 0x55, 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56, - 0xf4, 0x56, 0x44, 0x57, 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58, - 0xd1, 0x58, 0x21, 0x59, 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a, - 0xb0, 0x5a, 0x01, 0x5b, 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c, - 0x92, 0x5c, 0xe3, 0x5c, 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e, - 0x77, 0x5e, 0xc8, 0x5e, 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60, - 0x5e, 0x60, 0xb0, 0x60, 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61, - 0x48, 0x62, 0x9a, 0x62, 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63, - 0x34, 0x64, 0x87, 0x64, 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65, - 0x24, 0x66, 0x76, 0x66, 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67, - 0x15, 0x68, 0x69, 0x68, 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69, - 0x0a, 0x6a, 0x5e, 0x6a, 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b, - 0x01, 0x6c, 0x55, 0x6c, 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d, - 0xfb, 0x6d, 0x50, 0x6e, 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f, - 0xf8, 0x6f, 0x4d, 0x70, 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71, - 0xf7, 0x71, 0x4d, 0x72, 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73, - 0xfa, 0x73, 0x50, 0x74, 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75, - 0xff, 0x75, 0x55, 0x76, 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77, - 0x07, 0x78, 0x5e, 0x78, 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79, - 0x11, 0x7a, 0x69, 0x7a, 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b, - 0x1f, 0x7c, 0x77, 0x7c, 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d, - 0x2f, 0x7e, 0x88, 0x7e, 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f, - 0x42, 0x80, 0x9b, 0x80, 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81, - 0x59, 0x82, 0xb2, 0x82, 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84, - 0x72, 0x84, 0xcb, 0x84, 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86, - 0x8e, 0x86, 0xe8, 0x86, 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88, - 0xac, 0x88, 0x07, 0x89, 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a, - 0xce, 0x8a, 0x2a, 0x8b, 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c, - 0xf3, 0x8c, 0x4f, 0x8d, 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e, - 0x1b, 0x8f, 0x77, 0x8f, 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90, - 0x46, 0x91, 0xa2, 0x91, 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93, - 0x73, 0x93, 0xd1, 0x93, 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95, - 0xa4, 0x95, 0x02, 0x96, 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97, - 0xd8, 0x97, 0x36, 0x98, 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99, - 0x0f, 0x9a, 0x6e, 0x9a, 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b, - 0x49, 0x9c, 0xa8, 0x9c, 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e, - 0x86, 0x9e, 0xe6, 0x9e, 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0, - 0xc6, 0xa0, 0x27, 0xa1, 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2, - 0x0a, 0xa3, 0x6b, 0xa3, 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4, - 0x50, 0xa5, 0xb2, 0xa5, 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7, - 0x9a, 0xa7, 0xfc, 0xa7, 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9, - 0xe7, 0xa9, 0x49, 0xaa, 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab, - 0x37, 0xac, 0x9a, 0xac, 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae, - 0x8a, 0xae, 0xed, 0xae, 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0, - 0xe0, 0xb0, 0x45, 0xb1, 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2, - 0x3a, 0xb3, 0x9f, 0xb3, 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5, - 0x97, 0xb5, 0xfc, 0xb5, 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7, - 0xf7, 0xb7, 0x5d, 0xb8, 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9, - 0x5b, 0xba, 0xc1, 0xba, 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc, - 0xc2, 0xbc, 0x29, 0xbd, 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe, - 0x2c, 0xbf, 0x94, 0xbf, 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1, - 0x9a, 0xc1, 0x02, 0xc2, 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3, - 0x0b, 0xc4, 0x73, 0xc4, 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6, - 0x7f, 0xc6, 0xe8, 0xc6, 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8, - 0xf7, 0xc8, 0x60, 0xc9, 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb, - 0x72, 0xcb, 0xdc, 0xcb, 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd, - 0xf1, 0xcd, 0x5b, 0xce, 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0, - 0x73, 0xd0, 0xde, 0xd0, 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2, - 0xf8, 0xd2, 0x64, 0xd3, 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5, - 0x82, 0xd5, 0xee, 0xd5, 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7, - 0x0e, 0xd8, 0x7b, 0xd8, 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda, - 0x9e, 0xda, 0x0c, 0xdb, 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc, - 0x32, 0xdd, 0xa0, 0xdd, 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf, - 0xc9, 0xdf, 0x38, 0xe0, 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1, - 0x64, 0xe2, 0xd4, 0xe2, 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4, - 0x03, 0xe5, 0x73, 0xe5, 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7, - 0xa5, 0xe7, 0x16, 0xe8, 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9, - 0x4b, 0xea, 0xbc, 0xea, 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec, - 0xf5, 0xec, 0x66, 0xed, 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef, - 0xa2, 0xef, 0x14, 0xf0, 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1, - 0x53, 0xf2, 0xc6, 0xf2, 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4, - 0x07, 0xf5, 0x7b, 0xf5, 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7, - 0xc0, 0xf7, 0x34, 0xf8, 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa, - 0x7c, 0xfa, 0xf1, 0xfa, 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc, - 0x3c, 0xfd, 0xb2, 0xfd, 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff, - 0x98, 0x16, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5, - 0xb0, 0x00, 0x91, 0xe1, 0x1f, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01, + 0xee, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, 0xf4, 0xff, 0xff, 0x1a, + 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, 0xe5, 0xff, 0xff, 0xea, + 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, 0x01, 0x40, 0xd0, 0x14, + 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, + 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, 0xe0, 0xfe, 0xff, 0x0a, + 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, 0xd9, 0xff, 0xff, 0xea, + 0xdc, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1, + 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2, + 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xd1, 0xfe, 0xff, 0x0a, + 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea, + 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0xc9, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x00, 0x00, 0x24, 0x03, + 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f, 0xc8, 0x12, 0xe2, 0x15, + 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22, 0x28, 0x25, 0x26, 0x28, + 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33, 0xba, 0x36, 0x8c, 0x39, + 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44, 0x1c, 0x47, 0xb4, 0x49, + 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53, 0xf5, 0x55, 0x42, 0x58, + 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60, 0xf1, 0x62, 0xe8, 0x64, + 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c, 0xc9, 0x6d, 0x5e, 0x6f, + 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75, 0x41, 0x76, 0x6b, 0x77, + 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b, 0x29, 0x7c, 0xe3, 0x7c, + 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f, 0x61, 0x7f, 0xa6, 0x7f, + 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0, 0x8c, 0x10, 0x4f, 0xe2, + 0xb0, 0x00, 0x91, 0xe1, 0xa2, 0xfe, 0xff, 0xea, 0x00, 0x00, 0x3b, 0x00, + 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01, 0x64, 0x01, 0x9f, 0x01, + 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02, 0xca, 0x02, 0x05, 0x03, + 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03, 0x31, 0x04, 0x6e, 0x04, + 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05, 0x9b, 0x05, 0xd8, 0x05, + 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06, 0x07, 0x07, 0x43, 0x07, + 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08, 0x74, 0x08, 0xb1, 0x08, + 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09, 0xe4, 0x09, 0x21, 0x0a, + 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b, 0x56, 0x0b, 0x93, 0x0b, + 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c, 0xc9, 0x0c, 0x07, 0x0d, + 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e, 0x3f, 0x0e, 0x7d, 0x0e, + 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f, 0xb6, 0x0f, 0xf5, 0x0f, + 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10, 0x30, 0x11, 0x6f, 0x11, + 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12, 0xac, 0x12, 0xeb, 0x12, + 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13, 0x2a, 0x14, 0x6a, 0x14, + 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15, 0xaa, 0x15, 0xea, 0x15, + 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16, 0x2c, 0x17, 0x6c, 0x17, + 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18, 0xb0, 0x18, 0xf0, 0x18, + 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19, 0x36, 0x1a, 0x77, 0x1a, + 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b, 0xbe, 0x1b, 0x00, 0x1c, + 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d, 0x48, 0x1d, 0x8a, 0x1d, + 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e, 0xd5, 0x1e, 0x17, 0x1f, + 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20, 0x64, 0x20, 0xa6, 0x20, + 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21, 0xf5, 0x21, 0x38, 0x22, + 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23, 0x88, 0x23, 0xcb, 0x23, + 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24, 0x1d, 0x25, 0x61, 0x25, + 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26, 0xb4, 0x26, 0xf8, 0x26, + 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28, 0x4e, 0x28, 0x92, 0x28, + 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29, 0xea, 0x29, 0x2f, 0x2a, + 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b, 0x88, 0x2b, 0xcd, 0x2b, + 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c, 0x28, 0x2d, 0x6e, 0x2d, + 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e, 0xcb, 0x2e, 0x11, 0x2f, + 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30, 0x70, 0x30, 0xb6, 0x30, + 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31, 0x17, 0x32, 0x5e, 0x32, + 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33, 0xc1, 0x33, 0x08, 0x34, + 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35, 0x6c, 0x35, 0xb4, 0x35, + 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36, 0x1a, 0x37, 0x62, 0x37, + 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38, 0xcb, 0x38, 0x13, 0x39, + 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a, 0x7e, 0x3a, 0xc6, 0x3a, + 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b, 0x33, 0x3c, 0x7c, 0x3c, + 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d, 0xea, 0x3d, 0x34, 0x3e, + 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f, 0xa4, 0x3f, 0xee, 0x3f, + 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41, 0x61, 0x41, 0xab, 0x41, + 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42, 0x1f, 0x43, 0x6a, 0x43, + 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44, 0xe1, 0x44, 0x2c, 0x45, + 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46, 0xa4, 0x46, 0xf0, 0x46, + 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48, 0x6a, 0x48, 0xb6, 0x48, + 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49, 0x33, 0x4a, 0x7f, 0x4a, + 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b, 0xfe, 0x4b, 0x4a, 0x4c, + 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d, 0xcb, 0x4d, 0x18, 0x4e, + 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f, 0x9b, 0x4f, 0xe9, 0x4f, + 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51, 0x6e, 0x51, 0xbc, 0x51, + 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52, 0x43, 0x53, 0x91, 0x53, + 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54, 0x1a, 0x55, 0x69, 0x55, + 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56, 0xf4, 0x56, 0x44, 0x57, + 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58, 0xd1, 0x58, 0x21, 0x59, + 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a, 0xb0, 0x5a, 0x01, 0x5b, + 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c, 0x92, 0x5c, 0xe3, 0x5c, + 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e, 0x77, 0x5e, 0xc8, 0x5e, + 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60, 0x5e, 0x60, 0xb0, 0x60, + 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61, 0x48, 0x62, 0x9a, 0x62, + 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63, 0x34, 0x64, 0x87, 0x64, + 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65, 0x24, 0x66, 0x76, 0x66, + 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67, 0x15, 0x68, 0x69, 0x68, + 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69, 0x0a, 0x6a, 0x5e, 0x6a, + 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b, 0x01, 0x6c, 0x55, 0x6c, + 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d, 0xfb, 0x6d, 0x50, 0x6e, + 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f, 0xf8, 0x6f, 0x4d, 0x70, + 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71, 0xf7, 0x71, 0x4d, 0x72, + 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73, 0xfa, 0x73, 0x50, 0x74, + 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75, 0xff, 0x75, 0x55, 0x76, + 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77, 0x07, 0x78, 0x5e, 0x78, + 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79, 0x11, 0x7a, 0x69, 0x7a, + 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b, 0x1f, 0x7c, 0x77, 0x7c, + 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d, 0x2f, 0x7e, 0x88, 0x7e, + 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f, 0x42, 0x80, 0x9b, 0x80, + 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81, 0x59, 0x82, 0xb2, 0x82, + 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84, 0x72, 0x84, 0xcb, 0x84, + 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86, 0x8e, 0x86, 0xe8, 0x86, + 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88, 0xac, 0x88, 0x07, 0x89, + 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a, 0xce, 0x8a, 0x2a, 0x8b, + 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c, 0xf3, 0x8c, 0x4f, 0x8d, + 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e, 0x1b, 0x8f, 0x77, 0x8f, + 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90, 0x46, 0x91, 0xa2, 0x91, + 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93, 0x73, 0x93, 0xd1, 0x93, + 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95, 0xa4, 0x95, 0x02, 0x96, + 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97, 0xd8, 0x97, 0x36, 0x98, + 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99, 0x0f, 0x9a, 0x6e, 0x9a, + 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b, 0x49, 0x9c, 0xa8, 0x9c, + 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e, 0x86, 0x9e, 0xe6, 0x9e, + 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0, 0xc6, 0xa0, 0x27, 0xa1, + 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2, 0x0a, 0xa3, 0x6b, 0xa3, + 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4, 0x50, 0xa5, 0xb2, 0xa5, + 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7, 0x9a, 0xa7, 0xfc, 0xa7, + 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9, 0xe7, 0xa9, 0x49, 0xaa, + 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab, 0x37, 0xac, 0x9a, 0xac, + 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae, 0x8a, 0xae, 0xed, 0xae, + 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0, 0xe0, 0xb0, 0x45, 0xb1, + 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2, 0x3a, 0xb3, 0x9f, 0xb3, + 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5, 0x97, 0xb5, 0xfc, 0xb5, + 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7, 0xf7, 0xb7, 0x5d, 0xb8, + 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9, 0x5b, 0xba, 0xc1, 0xba, + 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc, 0xc2, 0xbc, 0x29, 0xbd, + 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe, 0x2c, 0xbf, 0x94, 0xbf, + 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1, 0x9a, 0xc1, 0x02, 0xc2, + 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3, 0x0b, 0xc4, 0x73, 0xc4, + 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6, 0x7f, 0xc6, 0xe8, 0xc6, + 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8, 0xf7, 0xc8, 0x60, 0xc9, + 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb, 0x72, 0xcb, 0xdc, 0xcb, + 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd, 0xf1, 0xcd, 0x5b, 0xce, + 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0, 0x73, 0xd0, 0xde, 0xd0, + 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2, 0xf8, 0xd2, 0x64, 0xd3, + 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5, 0x82, 0xd5, 0xee, 0xd5, + 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7, 0x0e, 0xd8, 0x7b, 0xd8, + 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda, 0x9e, 0xda, 0x0c, 0xdb, + 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc, 0x32, 0xdd, 0xa0, 0xdd, + 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf, 0xc9, 0xdf, 0x38, 0xe0, + 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1, 0x64, 0xe2, 0xd4, 0xe2, + 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4, 0x03, 0xe5, 0x73, 0xe5, + 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7, 0xa5, 0xe7, 0x16, 0xe8, + 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9, 0x4b, 0xea, 0xbc, 0xea, + 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec, 0xf5, 0xec, 0x66, 0xed, + 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef, 0xa2, 0xef, 0x14, 0xf0, + 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1, 0x53, 0xf2, 0xc6, 0xf2, + 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4, 0x07, 0xf5, 0x7b, 0xf5, + 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7, 0xc0, 0xf7, 0x34, 0xf8, + 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa, 0x7c, 0xfa, 0xf1, 0xfa, + 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc, 0x3c, 0xfd, 0xb2, 0xfd, + 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff, 0xa0, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5, 0xb0, 0x00, 0x91, 0xe1, + 0x1d, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -646,74 +649,73 @@ unsigned char bios_arm7_bin[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, - 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, - 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, - 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, - 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, - 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, - 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, - 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, - 0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, - 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, - 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a, - 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71, - 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20, - 0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, - 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, - 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31, - 0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, - 0x39, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40, - 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, - 0x71, 0x73, 0x74, 0x75, 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40, - 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, - 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, - 0x71, 0x72, 0x74, 0x75, 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40, - 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, - 0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f, - 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3, - 0xff, 0x20, 0xa0, 0xe3, 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3, - 0x05, 0x2c, 0x82, 0xe3, 0x62, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, - 0x01, 0x03, 0xc1, 0xe5, 0x5f, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, - 0x01, 0x03, 0xa0, 0xe3, 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, - 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2, - 0x00, 0x00, 0xd1, 0xe7, 0x56, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03, - 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, - 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, - 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, - 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, - 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, - 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, - 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, - 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, + 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a, + 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, + 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, + 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, + 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, + 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75, 0x76, + 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20, 0x21, 0x21, 0x21, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, + 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, + 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3a, 0x3b, + 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40, 0x41, 0x42, 0x43, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x73, 0x74, 0x75, + 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40, 0x41, 0x42, 0x43, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, 0x71, 0x72, 0x74, 0x75, + 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40, 0x41, 0x42, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75, + 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3, 0xff, 0x20, 0xa0, 0xe3, + 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3, 0x05, 0x2c, 0x82, 0xe3, + 0x60, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x01, 0x03, 0xc1, 0xe5, + 0x5d, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, + 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, + 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2, 0x00, 0x00, 0xd1, 0xe7, + 0x54, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, + 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, + 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, + 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, + 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, + 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, + 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, + 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1395,10 +1397,10 @@ unsigned char bios_arm7_bin[] = { 0x00, 0x00, 0x00, 0x00 }; -unsigned char bios_arm9_bin[] = { +std::array bios_arm9_bin = { 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea, - 0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, + 0xaf, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1422,17 +1424,17 @@ unsigned char bios_arm9_bin[] = { 0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5, 0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3, 0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1, - 0xf8, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, + 0x00, 0x07, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0x20, 0x02, 0xff, 0xff, 0x18, 0x02, 0xff, 0xff, 0xd4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0x4c, 0x02, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xa8, 0x02, 0xff, 0xff, 0x28, 0x03, 0xff, 0xff, 0x68, 0x03, 0xff, 0xff, 0xb4, 0x03, 0xff, 0xff, - 0x4c, 0x04, 0xff, 0xff, 0x54, 0x04, 0xff, 0xff, 0xe8, 0x04, 0xff, 0xff, - 0x64, 0x05, 0xff, 0xff, 0x10, 0x06, 0xff, 0xff, 0x14, 0x06, 0xff, 0xff, - 0x14, 0x06, 0xff, 0xff, 0x6c, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, - 0x98, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, + 0x54, 0x04, 0xff, 0xff, 0x5c, 0x04, 0xff, 0xff, 0xf0, 0x04, 0xff, 0xff, + 0x6c, 0x05, 0xff, 0xff, 0x18, 0x06, 0xff, 0xff, 0x1c, 0x06, 0xff, 0xff, + 0x1c, 0x06, 0xff, 0xff, 0x74, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, + 0xa0, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, - 0xc8, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, + 0xd0, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, 0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1, 0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xe3, 0x90, 0x0f, 0x07, 0xee, 0xf4, 0xff, 0xff, 0xea, @@ -1476,83 +1478,83 @@ unsigned char bios_arm9_bin[] = { 0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c, 0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44, 0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2, - 0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, + 0xff, 0x04, 0xc0, 0xe3, 0xff, 0x08, 0xc0, 0xe3, 0xa2, 0x20, 0xb0, 0xe1, + 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, 0x80, 0x50, 0x04, 0xe0, + 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, + 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, + 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, - 0x20, 0x00, 0xbd, 0xe8, 0x59, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, - 0x57, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, - 0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, - 0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, - 0x15, 0x5c, 0xa0, 0xe1, 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, - 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, - 0x83, 0x31, 0xa0, 0x01, 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, - 0x04, 0x00, 0x5c, 0xe3, 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, - 0x01, 0x60, 0xd0, 0x04, 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, - 0x36, 0x6c, 0xa0, 0xe1, 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, - 0x04, 0x90, 0x89, 0x10, 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, - 0x20, 0x00, 0x58, 0xe3, 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, - 0x00, 0x80, 0xa0, 0x03, 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, - 0xe0, 0x03, 0xbd, 0xe8, 0x32, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xb0, 0xe1, 0x2f, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x1f, 0xff, 0xff, 0x0a, - 0x01, 0xe0, 0x5e, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, - 0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a, - 0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, - 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x10, 0xff, 0xff, 0x0a, - 0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, - 0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, - 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, - 0x20, 0x00, 0xbd, 0x08, 0xf9, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, - 0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, - 0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, - 0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, + 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, 0x20, 0x00, 0xbd, 0xe8, + 0x57, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, 0x55, 0xff, 0xff, 0xea, + 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x02, 0xc0, 0xd2, 0xe5, + 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, 0xa4, 0x2f, 0xa0, 0xe1, + 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, 0x15, 0x5c, 0xa0, 0xe1, + 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, + 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, 0x83, 0x31, 0xa0, 0x01, + 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, 0x04, 0x00, 0x5c, 0xe3, + 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, 0x01, 0x60, 0xd0, 0x04, + 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, 0x36, 0x6c, 0xa0, 0xe1, + 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, 0x04, 0x90, 0x89, 0x10, + 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, 0x20, 0x00, 0x58, 0xe3, + 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, 0x00, 0x80, 0xa0, 0x03, + 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, 0xe0, 0x03, 0xbd, 0xe8, + 0x30, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, + 0x2d, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x1d, 0xff, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xed, 0xff, 0xff, 0x5a, + 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x14, 0xff, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, + 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, + 0x23, 0x24, 0xb0, 0xe1, 0x0e, 0xff, 0xff, 0x0a, 0x20, 0x00, 0x2d, 0xe9, + 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xdc, 0x04, + 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, - 0xeb, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, - 0xd9, 0xff, 0xff, 0xea, 0xe7, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, - 0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, - 0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, - 0xdc, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, - 0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xd4, 0xfe, 0xff, 0x0a, - 0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, - 0x04, 0x30, 0x90, 0xe4, 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1, - 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4, - 0xfa, 0xff, 0xff, 0x1a, 0xc6, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0xb2, 0xc0, 0xd0, 0xe0, 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0, - 0x01, 0x20, 0xc2, 0xe3, 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0, - 0x02, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0, - 0xfa, 0xff, 0xff, 0x1a, 0xba, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, - 0x00, 0x03, 0xc1, 0xe5, 0xb7, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, - 0x11, 0x0f, 0x19, 0xee, 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2, - 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, - 0x04, 0xf0, 0x5e, 0xe2, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, - 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, - 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, - 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, - 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, - 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, - 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, - 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf7, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, 0xf4, 0xff, 0xff, 0x1a, + 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, 0xe5, 0xff, 0xff, 0xea, + 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, 0x01, 0x40, 0xd0, 0x14, + 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, + 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, 0xe9, 0xfe, 0xff, 0x0a, + 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, 0xd9, 0xff, 0xff, 0xea, + 0xe5, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1, + 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2, + 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xda, 0xfe, 0xff, 0x0a, + 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea, + 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0xd2, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, + 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1, 0x01, 0xc0, 0xc1, 0xe4, + 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4, 0xfa, 0xff, 0xff, 0x1a, + 0xc4, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0xb2, 0xc0, 0xd0, 0xe0, + 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0, 0x01, 0x20, 0xc2, 0xe3, + 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0, 0x02, 0x20, 0x52, 0xe2, + 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0, 0xfa, 0xff, 0xff, 0x1a, + 0xb8, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x00, 0x03, 0xc1, 0xe5, + 0xb5, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x11, 0x0f, 0x19, 0xee, + 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2, 0x0f, 0xe0, 0xa0, 0xe1, + 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, + 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3, + 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2, + 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3, + 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1, + 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, + 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, + 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2, + 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1738,4 +1740,5 @@ unsigned char bios_arm9_bin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file +}; +} \ No newline at end of file diff --git a/src/FreeBIOS.h b/src/FreeBIOS.h index 82cb3694..a60c1f88 100644 --- a/src/FreeBIOS.h +++ b/src/FreeBIOS.h @@ -28,7 +28,13 @@ #ifndef FREEBIOS_H #define FREEBIOS_H -extern unsigned char bios_arm7_bin[16384]; -extern unsigned char bios_arm9_bin[4096]; +#include +#include "MemConstants.h" + +namespace melonDS +{ +extern std::array bios_arm7_bin; +extern std::array bios_arm9_bin; +} #endif // FREEBIOS_H diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 020219df..1be50e75 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -1,933 +1,878 @@ -/* - Copyright 2016-2022 melonDS team - - This file is part of melonDS. - - melonDS is free software: you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - melonDS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#include -#include -#include -#include "NDS.h" -#include "GBACart.h" -#include "CRC32.h" -#include "Platform.h" - -using Platform::Log; -using Platform::LogLevel; - -namespace GBACart -{ - -const char SOLAR_SENSOR_GAMECODES[10][5] = -{ - "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) - "U3IE", // Boktai - The Sun Is in Your Hand (USA) - "U3IP", // Boktai - The Sun Is in Your Hand (Europe) - "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) - "U32E", // Boktai 2 - Solar Boy Django (USA) - "U32P", // Boktai 2 - Solar Boy Django (Europe) - "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) - "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) -}; - -std::unique_ptr Cart; - -u16 OpenBusDecay; - - -CartCommon::CartCommon() -{ -} - -CartCommon::~CartCommon() -{ -} - -void CartCommon::Reset() -{ -} - -void CartCommon::DoSavestate(Savestate* file) -{ - file->Section("GBCS"); -} - -void CartCommon::SetupSave(u32 type) -{ -} - -void CartCommon::LoadSave(const u8* savedata, u32 savelen) -{ -} - -int CartCommon::SetInput(int num, bool pressed) -{ - return -1; -} - -u16 CartCommon::ROMRead(u32 addr) -{ - return 0; -} - -void CartCommon::ROMWrite(u32 addr, u16 val) -{ -} - -u8 CartCommon::SRAMRead(u32 addr) -{ - return 0; -} - -void CartCommon::SRAMWrite(u32 addr, u8 val) -{ -} - -u8* CartCommon::GetSaveMemory() const -{ - return nullptr; -} - -u32 CartCommon::GetSaveMemoryLength() const -{ - return 0; -} - -CartGame::CartGame(u8* rom, u32 len) : CartCommon() -{ - ROM = rom; - ROMLength = len; - - SRAM = nullptr; - SRAMLength = 0; - SRAMType = S_NULL; - SRAMFlashState = {}; -} - -CartGame::~CartGame() -{ - if (SRAM) delete[] SRAM; - delete[] ROM; -} - -u32 CartGame::Checksum() const -{ - u32 crc = CRC32(ROM, 0xC0, 0); - - // TODO: hash more contents? - - return crc; -} - -void CartGame::Reset() -{ - memset(&GPIO, 0, sizeof(GPIO)); -} - -void CartGame::DoSavestate(Savestate* file) -{ - CartCommon::DoSavestate(file); - - file->Var16(&GPIO.control); - file->Var16(&GPIO.data); - file->Var16(&GPIO.direction); - - u32 oldlen = SRAMLength; - - file->Var32(&SRAMLength); - - if (SRAMLength != oldlen) - { - // reallocate save memory - if (oldlen) delete[] SRAM; - SRAM = nullptr; - if (SRAMLength) SRAM = new u8[SRAMLength]; - } - if (SRAMLength) - { - // fill save memory if data is present - file->VarArray(SRAM, SRAMLength); - } - else - { - // no save data, clear the current state - SRAMType = SaveType::S_NULL; - SRAM = nullptr; - return; - } - - // persist some extra state info - file->Var8(&SRAMFlashState.bank); - file->Var8(&SRAMFlashState.cmd); - file->Var8(&SRAMFlashState.device); - file->Var8(&SRAMFlashState.manufacturer); - file->Var8(&SRAMFlashState.state); - - file->Var8((u8*)&SRAMType); - - if ((!file->Saving) && SRAM) - Platform::WriteGBASave(SRAM, SRAMLength, 0, SRAMLength); -} - -void CartGame::SetupSave(u32 type) -{ - if (SRAM) delete[] SRAM; - SRAM = nullptr; - - // TODO: have type be determined from some list, like in NDSCart - // and not this gross hack!! - SRAMLength = type; - - if (SRAMLength) - { - SRAM = new u8[SRAMLength]; - memset(SRAM, 0xFF, SRAMLength); - } - - switch (SRAMLength) - { - case 512: - SRAMType = S_EEPROM4K; - break; - case 8192: - SRAMType = S_EEPROM64K; - break; - case 32768: - SRAMType = S_SRAM256K; - break; - case 65536: - SRAMType = S_FLASH512K; - break; - case 128*1024: - SRAMType = S_FLASH1M; - break; - case 0: - SRAMType = S_NULL; - break; - default: - Log(LogLevel::Warn, "!! BAD GBA SAVE LENGTH %d\n", SRAMLength); - } - - if (SRAMType == S_FLASH512K) - { - // Panasonic 64K chip - SRAMFlashState.device = 0x1B; - SRAMFlashState.manufacturer = 0x32; - } - else if (SRAMType == S_FLASH1M) - { - // Sanyo 128K chip - SRAMFlashState.device = 0x13; - SRAMFlashState.manufacturer = 0x62; - } -} - -void CartGame::LoadSave(const u8* savedata, u32 savelen) -{ - if (!SRAM) return; - - u32 len = std::min(savelen, SRAMLength); - memcpy(SRAM, savedata, len); - Platform::WriteGBASave(savedata, len, 0, len); -} - -u16 CartGame::ROMRead(u32 addr) -{ - addr &= 0x01FFFFFF; - - if (addr >= 0xC4 && addr < 0xCA) - { - if (GPIO.control & 0x1) - { - switch (addr) - { - case 0xC4: return GPIO.data; - case 0xC6: return GPIO.direction; - case 0xC8: return GPIO.control; - } - } - else - return 0; - } - - // CHECKME: does ROM mirror? - if (addr < ROMLength) - return *(u16*)&ROM[addr]; - - return 0; -} - -void CartGame::ROMWrite(u32 addr, u16 val) -{ - addr &= 0x01FFFFFF; - - switch (addr) - { - case 0xC4: - GPIO.data &= ~GPIO.direction; - GPIO.data |= val & GPIO.direction; - ProcessGPIO(); - break; - - case 0xC6: - GPIO.direction = val; - break; - - case 0xC8: - GPIO.control = val; - break; - - default: - Log(LogLevel::Warn, "Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); - break; - } -} - -u8 CartGame::SRAMRead(u32 addr) -{ - addr &= 0xFFFF; - - switch (SRAMType) - { - case S_EEPROM4K: - case S_EEPROM64K: - return SRAMRead_EEPROM(addr); - - case S_FLASH512K: - case S_FLASH1M: - return SRAMRead_FLASH(addr); - - case S_SRAM256K: - return SRAMRead_SRAM(addr); - default: - break; - } - - return 0xFF; -} - -void CartGame::SRAMWrite(u32 addr, u8 val) -{ - addr &= 0xFFFF; - - switch (SRAMType) - { - case S_EEPROM4K: - case S_EEPROM64K: - return SRAMWrite_EEPROM(addr, val); - - case S_FLASH512K: - case S_FLASH1M: - return SRAMWrite_FLASH(addr, val); - - case S_SRAM256K: - return SRAMWrite_SRAM(addr, val); - default: - break; - } -} - -u8* CartGame::GetSaveMemory() const -{ - return SRAM; -} - -u32 CartGame::GetSaveMemoryLength() const -{ - return SRAMLength; -} - -void CartGame::ProcessGPIO() -{ -} - -u8 CartGame::SRAMRead_EEPROM(u32 addr) -{ - return 0; -} - -void CartGame::SRAMWrite_EEPROM(u32 addr, u8 val) -{ - // TODO: could be used in homebrew? -} - -// mostly ported from DeSmuME -u8 CartGame::SRAMRead_FLASH(u32 addr) -{ - if (SRAMFlashState.cmd == 0) // no cmd - { - return *(u8*)&SRAM[addr + 0x10000 * SRAMFlashState.bank]; - } - - switch (SRAMFlashState.cmd) - { - case 0x90: // chip ID - if (addr == 0x0000) return SRAMFlashState.manufacturer; - if (addr == 0x0001) return SRAMFlashState.device; - break; - case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - break; - case 0xA0: // write command - break; // ignore here, handled in Write_Flash() - case 0xB0: // bank switching (128K only) - break; // ignore here, handled in Write_Flash() - default: - Log(LogLevel::Warn, "GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlashState.cmd, addr); - break; - } - - return 0xFF; -} - -// mostly ported from DeSmuME -void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) -{ - switch (SRAMFlashState.state) - { - case 0x00: - if (addr == 0x5555) - { - if (val == 0xF0) - { - // reset - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - } - else if (val == 0xAA) - { - SRAMFlashState.state = 1; - return; - } - } - if (addr == 0x0000) - { - if (SRAMFlashState.cmd == 0xB0) - { - // bank switching - SRAMFlashState.bank = val; - SRAMFlashState.cmd = 0; - return; - } - } - break; - case 0x01: - if (addr == 0x2AAA && val == 0x55) - { - SRAMFlashState.state = 2; - return; - } - SRAMFlashState.state = 0; - break; - case 0x02: - if (addr == 0x5555) - { - // send command - switch (val) - { - case 0x80: // erase - SRAMFlashState.state = 0x80; - break; - case 0x90: // chip ID - SRAMFlashState.state = 0x90; - break; - case 0xA0: // write - SRAMFlashState.state = 0; - break; - default: - SRAMFlashState.state = 0; - break; - } - - SRAMFlashState.cmd = val; - return; - } - SRAMFlashState.state = 0; - break; - // erase - case 0x80: - if (addr == 0x5555 && val == 0xAA) - { - SRAMFlashState.state = 0x81; - return; - } - SRAMFlashState.state = 0; - break; - case 0x81: - if (addr == 0x2AAA && val == 0x55) - { - SRAMFlashState.state = 0x82; - return; - } - SRAMFlashState.state = 0; - break; - case 0x82: - if (val == 0x30) - { - u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; - memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); - - Platform::WriteGBASave(SRAM, SRAMLength, start_addr, 0x1000); - } - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - // chip ID - case 0x90: - if (addr == 0x5555 && val == 0xAA) - { - SRAMFlashState.state = 0x91; - return; - } - SRAMFlashState.state = 0; - break; - case 0x91: - if (addr == 0x2AAA && val == 0x55) - { - SRAMFlashState.state = 0x92; - return; - } - SRAMFlashState.state = 0; - break; - case 0x92: - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - default: - break; - } - - if (SRAMFlashState.cmd == 0xA0) // write - { - SRAMWrite_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - } - - Log(LogLevel::Debug, "GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n", - val, addr, SRAMFlashState.state); -} - -u8 CartGame::SRAMRead_SRAM(u32 addr) -{ - if (addr >= SRAMLength) return 0xFF; - - return SRAM[addr]; -} - -void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) -{ - if (addr >= SRAMLength) return; - - u8 prev = *(u8*)&SRAM[addr]; - if (prev != val) - { - *(u8*)&SRAM[addr] = val; - - // TODO: optimize this!! - Platform::WriteGBASave(SRAM, SRAMLength, addr, 1); - } -} - - -const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; - -CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len) -{ -} - -CartGameSolarSensor::~CartGameSolarSensor() -{ -} - -void CartGameSolarSensor::Reset() -{ - LightEdge = false; - LightCounter = 0; - LightSample = 0xFF; - LightLevel = 0; -} - -void CartGameSolarSensor::DoSavestate(Savestate* file) -{ - CartGame::DoSavestate(file); - - file->Var8((u8*)&LightEdge); - file->Var8(&LightCounter); - file->Var8(&LightSample); - file->Var8(&LightLevel); -} - -int CartGameSolarSensor::SetInput(int num, bool pressed) -{ - if (!pressed) return -1; - - if (num == Input_SolarSensorDown) - { - if (LightLevel > 0) - LightLevel--; - - return LightLevel; - } - else if (num == Input_SolarSensorUp) - { - if (LightLevel < 10) - LightLevel++; - - return LightLevel; - } - - return -1; -} - -void CartGameSolarSensor::ProcessGPIO() -{ - if (GPIO.data & 4) return; // Boktai chip select - if (GPIO.data & 2) // Reset - { - u8 prev = LightSample; - LightCounter = 0; - LightSample = (0xFF - (0x16 + kLuxLevels[LightLevel])); - Log(LogLevel::Debug, "Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); - } - if (GPIO.data & 1 && LightEdge) LightCounter++; - - LightEdge = !(GPIO.data & 1); - - bool sendBit = LightCounter >= LightSample; - if (GPIO.control & 1) - { - GPIO.data = (GPIO.data & GPIO.direction) | ((sendBit << 3) & ~GPIO.direction & 0xF); - } -} - - -CartRAMExpansion::CartRAMExpansion() : CartCommon() -{ -} - -CartRAMExpansion::~CartRAMExpansion() -{ -} - -void CartRAMExpansion::Reset() -{ - memset(RAM, 0xFF, sizeof(RAM)); - RAMEnable = 1; -} - -void CartRAMExpansion::DoSavestate(Savestate* file) -{ - CartCommon::DoSavestate(file); - - file->VarArray(RAM, sizeof(RAM)); - file->Var16(&RAMEnable); -} - -u16 CartRAMExpansion::ROMRead(u32 addr) -{ - addr &= 0x01FFFFFF; - - if (addr < 0x01000000) - { - switch (addr) - { - case 0xB0: return 0xFFFF; - case 0xB2: return 0x0000; - case 0xB4: return 0x2400; - case 0xB6: return 0x2424; - case 0xB8: return 0xFFFF; - case 0xBA: return 0xFFFF; - case 0xBC: return 0xFFFF; - case 0xBE: return 0x7FFF; - - case 0x1FFFC: return 0xFFFF; - case 0x1FFFE: return 0x7FFF; - - case 0x240000: return RAMEnable; - case 0x240002: return 0x0000; - } - - return 0xFFFF; - } - else if (addr < 0x01800000) - { - if (!RAMEnable) return 0xFFFF; - - return *(u16*)&RAM[addr & 0x7FFFFF]; - } - - return 0xFFFF; -} - -void CartRAMExpansion::ROMWrite(u32 addr, u16 val) -{ - addr &= 0x01FFFFFF; - - if (addr < 0x01000000) - { - switch (addr) - { - case 0x240000: - RAMEnable = val & 0x0001; - return; - } - } - else if (addr < 0x01800000) - { - if (!RAMEnable) return; - - *(u16*)&RAM[addr & 0x7FFFFF] = val; - } -} - - -bool Init() -{ - Cart = nullptr; - - return true; -} - -void DeInit() -{ - Cart = nullptr; -} - -void Reset() -{ - if (Cart) Cart->Reset(); -} - -void DoSavestate(Savestate* file) -{ - file->Section("GBAC"); // Game Boy Advance Cartridge - - // little state here - // no need to save OpenBusDecay, it will be set later - - u32 carttype = 0; - u32 cartchk = 0; - if (Cart) - { - carttype = Cart->Type(); - cartchk = Cart->Checksum(); - } - - if (file->Saving) - { - file->Var32(&carttype); - file->Var32(&cartchk); - } - else - { - u32 savetype; - file->Var32(&savetype); - if (savetype != carttype) return; - - u32 savechk; - file->Var32(&savechk); - if (savechk != cartchk) return; - } - - if (Cart) Cart->DoSavestate(file); -} - - -std::unique_ptr ParseROM(const u8* romdata, u32 romlen) -{ - if (romdata == nullptr) - { - Log(LogLevel::Error, "GBACart: romdata is null\n"); - return nullptr; - } - - if (romlen == 0) - { - Log(LogLevel::Error, "GBACart: romlen is zero\n"); - return nullptr; - } - - u32 cartromsize = 0x200; - while (cartromsize < romlen) - cartromsize <<= 1; - - u8* cartrom = nullptr; - try - { - cartrom = new u8[cartromsize]; - } - catch (const std::bad_alloc& e) - { - Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", cartromsize); - - return nullptr; - } - - memset(cartrom, 0, cartromsize); - memcpy(cartrom, romdata, romlen); - - char gamecode[5] = { '\0' }; - memcpy(&gamecode, cartrom + 0xAC, 4); - - bool solarsensor = false; - for (const char* i : SOLAR_SENSOR_GAMECODES) - { - if (strcmp(gamecode, i) == 0) - solarsensor = true; - } - - if (solarsensor) - { - Log(LogLevel::Info, "GBA solar sensor support detected!\n"); - } - - std::unique_ptr cart; - if (solarsensor) - cart = std::make_unique(cartrom, cartromsize); - else - cart = std::make_unique(cartrom, cartromsize); - - cart->Reset(); - - // TODO: setup cart save here! from a list or something - - // save - //printf("GBA save file: %s\n", sram); - - // TODO: have a list of sorts like in NDSCart? to determine the savemem type - //if (Cart) Cart->LoadSave(sram, 0); - - return cart; -} - -bool InsertROM(std::unique_ptr&& cart) -{ - if (!cart) { - Log(LogLevel::Error, "Failed to insert invalid GBA cart; existing cart (if any) was not ejected.\n"); - return false; - } - - if (Cart != nullptr) - EjectCart(); - - Cart = std::move(cart); - - const u8* cartrom = Cart->GetROM(); - - if (cartrom) - { - char gamecode[5] = { '\0' }; - memcpy(&gamecode, Cart->GetROM() + 0xAC, 4); - Log(LogLevel::Info, "Inserted GBA cart with game code: %s\n", gamecode); - } - else - { - Log(LogLevel::Info, "Inserted GBA cart with no game code (it's probably an accessory)\n"); - } - - return true; -} - -bool LoadROM(const u8* romdata, u32 romlen) -{ - std::unique_ptr data = ParseROM(romdata, romlen); - - return InsertROM(std::move(data)); -} - -void LoadSave(const u8* savedata, u32 savelen) -{ - if (Cart) - { - // gross hack - Cart->SetupSave(savelen); - - Cart->LoadSave(savedata, savelen); - } -} - -void LoadAddon(int type) -{ - switch (type) - { - case NDS::GBAAddon_RAMExpansion: - Cart = std::make_unique(); - break; - - default: - Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); - return; - } -} - -void EjectCart() -{ - Cart = nullptr; -} - - -int SetInput(int num, bool pressed) -{ - if (Cart) return Cart->SetInput(num, pressed); - - return -1; -} - - -void SetOpenBusDecay(u16 val) -{ - OpenBusDecay = val; -} - - -u16 ROMRead(u32 addr) -{ - if (Cart) return Cart->ROMRead(addr); - - return ((addr >> 1) & 0xFFFF) | OpenBusDecay; -} - -void ROMWrite(u32 addr, u16 val) -{ - if (Cart) Cart->ROMWrite(addr, val); -} - -u8 SRAMRead(u32 addr) -{ - if (Cart) return Cart->SRAMRead(addr); - - return 0xFF; -} - -void SRAMWrite(u32 addr, u8 val) -{ - if (Cart) Cart->SRAMWrite(addr, val); -} - -u8* GetSaveMemory() -{ - return Cart ? Cart->GetSaveMemory() : nullptr; -} - -u32 GetSaveMemoryLength() -{ - return Cart ? Cart->GetSaveMemoryLength() : 0; -} - -} +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include "NDS.h" +#include "GBACart.h" +#include "CRC32.h" +#include "Platform.h" +#include "Utils.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; + +namespace GBACart +{ + +const char SOLAR_SENSOR_GAMECODES[10][5] = +{ + "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) + "U3IE", // Boktai - The Sun Is in Your Hand (USA) + "U3IP", // Boktai - The Sun Is in Your Hand (Europe) + "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) + "U32E", // Boktai 2 - Solar Boy Django (USA) + "U32P", // Boktai 2 - Solar Boy Django (Europe) + "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) + "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) +}; + +CartCommon::CartCommon(GBACart::CartType type) : CartType(type) +{ +} + +void CartCommon::Reset() +{ +} + +void CartCommon::DoSavestate(Savestate* file) +{ + file->Section("GBCS"); +} + +void CartCommon::SetSaveMemory(const u8* savedata, u32 savelen) +{ +} + +int CartCommon::SetInput(int num, bool pressed) +{ + return -1; +} + +u16 CartCommon::ROMRead(u32 addr) const +{ + return 0; +} + +void CartCommon::ROMWrite(u32 addr, u16 val) +{ +} + +u8 CartCommon::SRAMRead(u32 addr) +{ + return 0; +} + +void CartCommon::SRAMWrite(u32 addr, u8 val) +{ +} + +u8* CartCommon::GetSaveMemory() const +{ + return nullptr; +} + +u32 CartCommon::GetSaveMemoryLength() const +{ + return 0; +} + +CartGame::CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type) : + CartGame(CopyToUnique(rom, len), len, CopyToUnique(sram, sramlen), sramlen, type) +{ +} + +CartGame::CartGame(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen, GBACart::CartType type) : + CartCommon(type), + ROM(std::move(rom)), + ROMLength(len), + SRAM(std::move(sram)), + SRAMLength(sramlen) +{ + if (SRAM && SRAMLength) + { + SetupSave(sramlen); + } +} + +CartGame::~CartGame() = default; +// unique_ptr cleans up the allocated memory + +u32 CartGame::Checksum() const +{ + u32 crc = CRC32(ROM.get(), 0xC0, 0); + + // TODO: hash more contents? + + return crc; +} + +void CartGame::Reset() +{ + memset(&GPIO, 0, sizeof(GPIO)); +} + +void CartGame::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->Var16(&GPIO.control); + file->Var16(&GPIO.data); + file->Var16(&GPIO.direction); + + u32 oldlen = SRAMLength; + + file->Var32(&SRAMLength); + + if (SRAMLength != oldlen) + { + // reallocate save memory + SRAM = SRAMLength ? std::make_unique(SRAMLength) : nullptr; + } + if (SRAMLength) + { + // fill save memory if data is present + file->VarArray(SRAM.get(), SRAMLength); + } + else + { + // no save data, clear the current state + SRAMType = SaveType::S_NULL; + SRAM = nullptr; + return; + } + + // persist some extra state info + file->Var8(&SRAMFlashState.bank); + file->Var8(&SRAMFlashState.cmd); + file->Var8(&SRAMFlashState.device); + file->Var8(&SRAMFlashState.manufacturer); + file->Var8(&SRAMFlashState.state); + + file->Var8((u8*)&SRAMType); + + if ((!file->Saving) && SRAM) + Platform::WriteGBASave(SRAM.get(), SRAMLength, 0, SRAMLength); +} + +void CartGame::SetupSave(u32 type) +{ + // TODO: have type be determined from some list, like in NDSCart + // and not this gross hack!! + SRAMLength = type; + switch (SRAMLength) + { + case 512: + SRAMType = S_EEPROM4K; + break; + case 8192: + SRAMType = S_EEPROM64K; + break; + case 32768: + SRAMType = S_SRAM256K; + break; + case 65536: + SRAMType = S_FLASH512K; + break; + case 128*1024: + case (128*1024 + 0x10): // .sav file with appended real time clock data (ex: emulator mGBA) + SRAMType = S_FLASH1M; + break; + case 0: + SRAMType = S_NULL; + break; + default: + Log(LogLevel::Warn, "!! BAD GBA SAVE LENGTH %d\n", SRAMLength); + } + + if (SRAMType == S_FLASH512K) + { + // Panasonic 64K chip + SRAMFlashState.device = 0x1B; + SRAMFlashState.manufacturer = 0x32; + } + else if (SRAMType == S_FLASH1M) + { + // Sanyo 128K chip + SRAMFlashState.device = 0x13; + SRAMFlashState.manufacturer = 0x62; + } +} + +void CartGame::SetSaveMemory(const u8* savedata, u32 savelen) +{ + SetupSave(savelen); + + u32 len = std::min(savelen, SRAMLength); + memcpy(SRAM.get(), savedata, len); + Platform::WriteGBASave(savedata, len, 0, len); +} + +u16 CartGame::ROMRead(u32 addr) const +{ + addr &= 0x01FFFFFF; + + if (addr >= 0xC4 && addr < 0xCA) + { + if (GPIO.control & 0x1) + { + switch (addr) + { + case 0xC4: return GPIO.data; + case 0xC6: return GPIO.direction; + case 0xC8: return GPIO.control; + } + } + else + return 0; + } + + // CHECKME: does ROM mirror? + if (addr < ROMLength) + return *(u16*)&ROM[addr]; + + return 0; +} + +void CartGame::ROMWrite(u32 addr, u16 val) +{ + addr &= 0x01FFFFFF; + + switch (addr) + { + case 0xC4: + GPIO.data &= ~GPIO.direction; + GPIO.data |= val & GPIO.direction; + ProcessGPIO(); + break; + + case 0xC6: + GPIO.direction = val; + break; + + case 0xC8: + GPIO.control = val; + break; + + default: + Log(LogLevel::Warn, "Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); + break; + } +} + +u8 CartGame::SRAMRead(u32 addr) +{ + addr &= 0xFFFF; + + switch (SRAMType) + { + case S_EEPROM4K: + case S_EEPROM64K: + return SRAMRead_EEPROM(addr); + + case S_FLASH512K: + case S_FLASH1M: + return SRAMRead_FLASH(addr); + + case S_SRAM256K: + return SRAMRead_SRAM(addr); + default: + break; + } + + return 0xFF; +} + +void CartGame::SRAMWrite(u32 addr, u8 val) +{ + addr &= 0xFFFF; + + switch (SRAMType) + { + case S_EEPROM4K: + case S_EEPROM64K: + return SRAMWrite_EEPROM(addr, val); + + case S_FLASH512K: + case S_FLASH1M: + return SRAMWrite_FLASH(addr, val); + + case S_SRAM256K: + return SRAMWrite_SRAM(addr, val); + default: + break; + } +} + +u8* CartGame::GetSaveMemory() const +{ + return SRAM.get(); +} + +u32 CartGame::GetSaveMemoryLength() const +{ + return SRAMLength; +} + +void CartGame::ProcessGPIO() +{ +} + +u8 CartGame::SRAMRead_EEPROM(u32 addr) +{ + return 0; +} + +void CartGame::SRAMWrite_EEPROM(u32 addr, u8 val) +{ + // TODO: could be used in homebrew? +} + +// mostly ported from DeSmuME +u8 CartGame::SRAMRead_FLASH(u32 addr) +{ + if (SRAMFlashState.cmd == 0) // no cmd + { + return *(u8*)&SRAM[addr + 0x10000 * SRAMFlashState.bank]; + } + + switch (SRAMFlashState.cmd) + { + case 0x90: // chip ID + if (addr == 0x0000) return SRAMFlashState.manufacturer; + if (addr == 0x0001) return SRAMFlashState.device; + break; + case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + break; + case 0xA0: // write command + break; // ignore here, handled in Write_Flash() + case 0xB0: // bank switching (128K only) + break; // ignore here, handled in Write_Flash() + default: + Log(LogLevel::Warn, "GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlashState.cmd, addr); + break; + } + + return 0xFF; +} + +// mostly ported from DeSmuME +void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) +{ + switch (SRAMFlashState.state) + { + case 0x00: + if (addr == 0x5555) + { + if (val == 0xF0) + { + // reset + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + } + else if (val == 0xAA) + { + SRAMFlashState.state = 1; + return; + } + } + if (addr == 0x0000) + { + if (SRAMFlashState.cmd == 0xB0) + { + // bank switching + SRAMFlashState.bank = val; + SRAMFlashState.cmd = 0; + return; + } + } + break; + case 0x01: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlashState.state = 2; + return; + } + SRAMFlashState.state = 0; + break; + case 0x02: + if (addr == 0x5555) + { + // send command + switch (val) + { + case 0x80: // erase + SRAMFlashState.state = 0x80; + break; + case 0x90: // chip ID + SRAMFlashState.state = 0x90; + break; + case 0xA0: // write + SRAMFlashState.state = 0; + break; + default: + SRAMFlashState.state = 0; + break; + } + + SRAMFlashState.cmd = val; + return; + } + SRAMFlashState.state = 0; + break; + // erase + case 0x80: + if (addr == 0x5555 && val == 0xAA) + { + SRAMFlashState.state = 0x81; + return; + } + SRAMFlashState.state = 0; + break; + case 0x81: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlashState.state = 0x82; + return; + } + SRAMFlashState.state = 0; + break; + case 0x82: + if (val == 0x30) + { + u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; + memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); + + Platform::WriteGBASave(SRAM.get(), SRAMLength, start_addr, 0x1000); + } + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + // chip ID + case 0x90: + if (addr == 0x5555 && val == 0xAA) + { + SRAMFlashState.state = 0x91; + return; + } + SRAMFlashState.state = 0; + break; + case 0x91: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlashState.state = 0x92; + return; + } + SRAMFlashState.state = 0; + break; + case 0x92: + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + default: + break; + } + + if (SRAMFlashState.cmd == 0xA0) // write + { + SRAMWrite_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + } + + Log(LogLevel::Debug, "GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n", + val, addr, SRAMFlashState.state); +} + +u8 CartGame::SRAMRead_SRAM(u32 addr) +{ + if (addr >= SRAMLength) return 0xFF; + + return SRAM[addr]; +} + +void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) +{ + if (addr >= SRAMLength) return; + + u8 prev = *(u8*)&SRAM[addr]; + if (prev != val) + { + *(u8*)&SRAM[addr] = val; + + // TODO: optimize this!! + Platform::WriteGBASave(SRAM.get(), SRAMLength, addr, 1); + } +} + + +CartGameSolarSensor::CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen) : + CartGameSolarSensor(CopyToUnique(rom, len), len, CopyToUnique(sram, sramlen), sramlen) +{ +} + +CartGameSolarSensor::CartGameSolarSensor(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen) : + CartGame(std::move(rom), len, std::move(sram), sramlen, CartType::GameSolarSensor) +{ +} + +const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; + +void CartGameSolarSensor::Reset() +{ + CartGame::Reset(); + LightEdge = false; + LightCounter = 0; + LightSample = 0xFF; + LightLevel = 0; +} + +void CartGameSolarSensor::DoSavestate(Savestate* file) +{ + CartGame::DoSavestate(file); + + file->Var8((u8*)&LightEdge); + file->Var8(&LightCounter); + file->Var8(&LightSample); + file->Var8(&LightLevel); +} + +int CartGameSolarSensor::SetInput(int num, bool pressed) +{ + if (!pressed) return -1; + + if (num == Input_SolarSensorDown) + { + if (LightLevel > 0) + LightLevel--; + + return LightLevel; + } + else if (num == Input_SolarSensorUp) + { + if (LightLevel < 10) + LightLevel++; + + return LightLevel; + } + + return -1; +} + +void CartGameSolarSensor::ProcessGPIO() +{ + if (GPIO.data & 4) return; // Boktai chip select + if (GPIO.data & 2) // Reset + { + u8 prev = LightSample; + LightCounter = 0; + LightSample = (0xFF - (0x16 + kLuxLevels[LightLevel])); + Log(LogLevel::Debug, "Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); + } + if (GPIO.data & 1 && LightEdge) LightCounter++; + + LightEdge = !(GPIO.data & 1); + + bool sendBit = LightCounter >= LightSample; + if (GPIO.control & 1) + { + GPIO.data = (GPIO.data & GPIO.direction) | ((sendBit << 3) & ~GPIO.direction & 0xF); + } +} + + +CartRAMExpansion::CartRAMExpansion() : CartCommon(RAMExpansion) +{ +} + +CartRAMExpansion::~CartRAMExpansion() = default; + +void CartRAMExpansion::Reset() +{ + memset(RAM, 0xFF, sizeof(RAM)); + RAMEnable = 1; +} + +void CartRAMExpansion::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->VarArray(RAM, sizeof(RAM)); + file->Var16(&RAMEnable); +} + +u16 CartRAMExpansion::ROMRead(u32 addr) const +{ + addr &= 0x01FFFFFF; + + if (addr < 0x01000000) + { + switch (addr) + { + case 0xB0: return 0xFFFF; + case 0xB2: return 0x0000; + case 0xB4: return 0x2400; + case 0xB6: return 0x2424; + case 0xB8: return 0xFFFF; + case 0xBA: return 0xFFFF; + case 0xBC: return 0xFFFF; + case 0xBE: return 0x7FFF; + + case 0x1FFFC: return 0xFFFF; + case 0x1FFFE: return 0x7FFF; + + case 0x240000: return RAMEnable; + case 0x240002: return 0x0000; + } + + return 0xFFFF; + } + else if (addr < 0x01800000) + { + if (!RAMEnable) return 0xFFFF; + + return *(u16*)&RAM[addr & 0x7FFFFF]; + } + + return 0xFFFF; +} + +void CartRAMExpansion::ROMWrite(u32 addr, u16 val) +{ + addr &= 0x01FFFFFF; + + if (addr < 0x01000000) + { + switch (addr) + { + case 0x240000: + RAMEnable = val & 0x0001; + return; + } + } + else if (addr < 0x01800000) + { + if (!RAMEnable) return; + + *(u16*)&RAM[addr & 0x7FFFFF] = val; + } +} + +GBACartSlot::GBACartSlot(std::unique_ptr&& cart) noexcept : Cart(std::move(cart)) +{ +} + +void GBACartSlot::Reset() noexcept +{ + if (Cart) Cart->Reset(); +} + +void GBACartSlot::DoSavestate(Savestate* file) noexcept +{ + file->Section("GBAC"); // Game Boy Advance Cartridge + + // little state here + // no need to save OpenBusDecay, it will be set later + + u32 carttype = 0; + u32 cartchk = 0; + if (Cart) + { + carttype = Cart->Type(); + cartchk = Cart->Checksum(); + } + + if (file->Saving) + { + file->Var32(&carttype); + file->Var32(&cartchk); + } + else + { + u32 savetype; + file->Var32(&savetype); + if (savetype != carttype) return; + + u32 savechk; + file->Var32(&savechk); + if (savechk != cartchk) return; + } + + if (Cart) Cart->DoSavestate(file); +} + +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen) +{ + return ParseROM(std::move(romdata), romlen, nullptr, 0); +} + +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen) +{ + auto [romcopy, romcopylen] = PadToPowerOf2(romdata, romlen); + + return ParseROM(std::move(romcopy), romcopylen, CopyToUnique(sramdata, sramlen), sramlen); +} + +std::unique_ptr ParseROM(const u8* romdata, u32 romlen) +{ + return ParseROM(romdata, romlen, nullptr, 0); +} + +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::unique_ptr&& sramdata, u32 sramlen) +{ + if (romdata == nullptr) + { + Log(LogLevel::Error, "GBACart: romdata is null\n"); + return nullptr; + } + + if (romlen == 0) + { + Log(LogLevel::Error, "GBACart: romlen is zero\n"); + return nullptr; + } + + auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen); + + char gamecode[5] = { '\0' }; + memcpy(&gamecode, cartrom.get() + 0xAC, 4); + + bool solarsensor = false; + for (const char* i : SOLAR_SENSOR_GAMECODES) + { + if (strcmp(gamecode, i) == 0) + solarsensor = true; + } + + if (solarsensor) + { + Log(LogLevel::Info, "GBA solar sensor support detected!\n"); + } + + std::unique_ptr cart; + if (solarsensor) + cart = std::make_unique(std::move(cartrom), cartromsize, std::move(sramdata), sramlen); + else + cart = std::make_unique(std::move(cartrom), cartromsize, std::move(sramdata), sramlen); + + cart->Reset(); + + // save + //printf("GBA save file: %s\n", sram); + + // TODO: have a list of sorts like in NDSCart? to determine the savemem type + //if (Cart) Cart->LoadSave(sram, 0); + + return cart; +} + +void GBACartSlot::SetCart(std::unique_ptr&& cart) noexcept +{ + Cart = std::move(cart); + + if (!Cart) + { + Log(LogLevel::Info, "Ejected GBA cart"); + return; + } + + const u8* cartrom = Cart->GetROM(); + + if (cartrom) + { + char gamecode[5] = { '\0' }; + memcpy(&gamecode, Cart->GetROM() + 0xAC, 4); + Log(LogLevel::Info, "Inserted GBA cart with game code: %s\n", gamecode); + } + else + { + Log(LogLevel::Info, "Inserted GBA cart with no game code (it's probably an accessory)\n"); + } +} + +void GBACartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept +{ + if (Cart) + { + Cart->SetSaveMemory(savedata, savelen); + } +} + +void GBACartSlot::LoadAddon(int type) noexcept +{ + switch (type) + { + case GBAAddon_RAMExpansion: + Cart = std::make_unique(); + break; + + default: + Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); + return; + } +} + +std::unique_ptr GBACartSlot::EjectCart() noexcept +{ + return std::move(Cart); + // Cart will be nullptr after this function returns, due to the move +} + + +int GBACartSlot::SetInput(int num, bool pressed) noexcept +{ + if (Cart) return Cart->SetInput(num, pressed); + + return -1; +} + + +u16 GBACartSlot::ROMRead(u32 addr) const noexcept +{ + if (Cart) return Cart->ROMRead(addr); + + return ((addr >> 1) & 0xFFFF) | OpenBusDecay; +} + +void GBACartSlot::ROMWrite(u32 addr, u16 val) noexcept +{ + if (Cart) Cart->ROMWrite(addr, val); +} + +u8 GBACartSlot::SRAMRead(u32 addr) noexcept +{ + if (Cart) return Cart->SRAMRead(addr); + + return 0xFF; +} + +void GBACartSlot::SRAMWrite(u32 addr, u8 val) noexcept +{ + if (Cart) Cart->SRAMWrite(addr, val); +} + +} + +} \ No newline at end of file diff --git a/src/GBACart.h b/src/GBACart.h index 1c7bfb4d..493bf6b8 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -1,257 +1,289 @@ -/* - Copyright 2016-2022 melonDS team - - This file is part of melonDS. - - melonDS is free software: you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - melonDS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#ifndef GBACART_H -#define GBACART_H - -#include -#include "types.h" -#include "Savestate.h" - -namespace GBACart -{ - -enum CartType -{ - Default = 0x001, - Game = 0x101, - GameSolarSensor = 0x102, - RAMExpansion = 0x201, -}; - -// CartCommon -- base code shared by all cart types -class CartCommon -{ -public: - CartCommon(); - virtual ~CartCommon(); - - virtual u32 Type() const = 0; - virtual u32 Checksum() const { return 0; } - - virtual void Reset(); - - virtual void DoSavestate(Savestate* file); - - virtual void SetupSave(u32 type); - virtual void LoadSave(const u8* savedata, u32 savelen); - - virtual int SetInput(int num, bool pressed); - - virtual u16 ROMRead(u32 addr); - virtual void ROMWrite(u32 addr, u16 val); - - virtual u8 SRAMRead(u32 addr); - virtual void SRAMWrite(u32 addr, u8 val); - - [[nodiscard]] virtual const u8* GetROM() const { return nullptr; } - [[nodiscard]] virtual u32 GetROMLength() const { return 0; } - - virtual u8* GetSaveMemory() const; - virtual u32 GetSaveMemoryLength() const; -}; - -// CartGame -- regular retail game cart (ROM, SRAM) -class CartGame : public CartCommon -{ -public: - CartGame(u8* rom, u32 len); - virtual ~CartGame() override; - - virtual u32 Type() const override { return CartType::Game; } - virtual u32 Checksum() const override; - - virtual void Reset() override; - - virtual void DoSavestate(Savestate* file) override; - - virtual void SetupSave(u32 type) override; - virtual void LoadSave(const u8* savedata, u32 savelen) override; - - virtual u16 ROMRead(u32 addr) override; - virtual void ROMWrite(u32 addr, u16 val) override; - - virtual u8 SRAMRead(u32 addr) override; - virtual void SRAMWrite(u32 addr, u8 val) override; - - [[nodiscard]] const u8* GetROM() const override { return ROM; } - [[nodiscard]] u32 GetROMLength() const override { return ROMLength; } - - virtual u8* GetSaveMemory() const override; - virtual u32 GetSaveMemoryLength() const override; -protected: - virtual void ProcessGPIO(); - - u8 SRAMRead_EEPROM(u32 addr); - void SRAMWrite_EEPROM(u32 addr, u8 val); - u8 SRAMRead_FLASH(u32 addr); - void SRAMWrite_FLASH(u32 addr, u8 val); - u8 SRAMRead_SRAM(u32 addr); - void SRAMWrite_SRAM(u32 addr, u8 val); - - u8* ROM; - u32 ROMLength; - - struct - { - u16 data; - u16 direction; - u16 control; - - } GPIO; - - enum SaveType - { - S_NULL, - S_EEPROM4K, - S_EEPROM64K, - S_SRAM256K, - S_FLASH512K, - S_FLASH1M - }; - - // from DeSmuME - struct - { - u8 state; - u8 cmd; - u8 device; - u8 manufacturer; - u8 bank; - - } SRAMFlashState; - - u8* SRAM; - u32 SRAMLength; - SaveType SRAMType; -}; - -// CartGameSolarSensor -- Boktai game cart -class CartGameSolarSensor : public CartGame -{ -public: - CartGameSolarSensor(u8* rom, u32 len); - virtual ~CartGameSolarSensor() override; - - virtual u32 Type() const override { return CartType::GameSolarSensor; } - - virtual void Reset() override; - - virtual void DoSavestate(Savestate* file) override; - - virtual int SetInput(int num, bool pressed) override; - -private: - virtual void ProcessGPIO() override; - - static const int kLuxLevels[11]; - - bool LightEdge; - u8 LightCounter; - u8 LightSample; - u8 LightLevel; -}; - -// CartRAMExpansion -- RAM expansion cart (DS browser, ...) -class CartRAMExpansion : public CartCommon -{ -public: - CartRAMExpansion(); - ~CartRAMExpansion() override; - - virtual u32 Type() const override { return CartType::RAMExpansion; } - - void Reset() override; - - void DoSavestate(Savestate* file) override; - - u16 ROMRead(u32 addr) override; - void ROMWrite(u32 addr, u16 val) override; - -private: - u8 RAM[0x800000]; - u16 RAMEnable; -}; - -// possible inputs for GBA carts that might accept user input -enum -{ - Input_SolarSensorDown = 0, - Input_SolarSensorUp, -}; - -extern std::unique_ptr Cart; - -bool Init(); -void DeInit(); -void Reset(); - -void DoSavestate(Savestate* file); - -/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass -/// that can be inserted into the emulator or used to extract information about the cart beforehand. -/// @param romdata The ROM data to parse. -/// The returned cartridge will contain a copy of this data, -/// so the caller may deallocate \c romdata after this function returns. -/// @param romlen The length of the ROM data in bytes. -/// @returns A \c GBACart::CartCommon object representing the parsed ROM, -/// or \c nullptr if the ROM data couldn't be parsed. -std::unique_ptr ParseROM(const u8* romdata, u32 romlen); - -/// Applies the GBACartData to the emulator state and unloads an existing ROM if any. -/// Upon successful insertion, \c cart will be nullptr and the global GBACart state -/// (\c CartROM, CartInserted, etc.) will be updated. -bool InsertROM(std::unique_ptr&& cart); -bool LoadROM(const u8* romdata, u32 romlen); -void LoadSave(const u8* savedata, u32 savelen); - -void LoadAddon(int type); - -void EjectCart(); - -//bool LoadROM(const char* path, const char* sram); -//bool LoadROM(const u8* romdata, u32 filelength, const char *sram); -//void RelocateSave(const char* path, bool write); - -// TODO: make more flexible, support nonbinary inputs -int SetInput(int num, bool pressed); - -void SetOpenBusDecay(u16 val); - -u16 ROMRead(u32 addr); -void ROMWrite(u32 addr, u16 val); - -u8 SRAMRead(u32 addr); -void SRAMWrite(u32 addr, u8 val); - -/// This function is intended to allow frontends to save and load SRAM -/// without using melonDS APIs. -/// Modifying the emulated SRAM for any other reason is strongly discouraged. -/// The returned pointer may be invalidated if the emulator is reset, -/// or when a new game is loaded. -/// Consequently, don't store the returned pointer for any longer than necessary. -/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. -u8* GetSaveMemory(); - -/// @returns The length of the buffer returned by ::GetSaveMemory() -/// if a cart is loaded and supports SRAM, otherwise zero. -u32 GetSaveMemoryLength(); - -} - -#endif // GBACART_H +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef GBACART_H +#define GBACART_H + +#include +#include "types.h" +#include "Savestate.h" + +namespace melonDS::GBACart +{ + +enum CartType +{ + Default = 0x001, + Game = 0x101, + GameSolarSensor = 0x102, + RAMExpansion = 0x201, +}; + +// CartCommon -- base code shared by all cart types +class CartCommon +{ +public: + virtual ~CartCommon() = default; + + [[nodiscard]] u32 Type() const { return CartType; } + virtual u32 Checksum() const { return 0; } + + virtual void Reset(); + + virtual void DoSavestate(Savestate* file); + + virtual int SetInput(int num, bool pressed); + + virtual u16 ROMRead(u32 addr) const; + virtual void ROMWrite(u32 addr, u16 val); + + virtual u8 SRAMRead(u32 addr); + virtual void SRAMWrite(u32 addr, u8 val); + + [[nodiscard]] virtual const u8* GetROM() const { return nullptr; } + [[nodiscard]] virtual u32 GetROMLength() const { return 0; } + + virtual u8* GetSaveMemory() const; + virtual u32 GetSaveMemoryLength() const; + virtual void SetSaveMemory(const u8* savedata, u32 savelen); +protected: + CartCommon(GBACart::CartType type); + friend class GBACartSlot; +private: + GBACart::CartType CartType; +}; + +// CartGame -- regular retail game cart (ROM, SRAM) +class CartGame : public CartCommon +{ +public: + CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game); + CartGame(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game); + ~CartGame() override; + + u32 Checksum() const override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + void ROMWrite(u32 addr, u16 val) override; + + u8 SRAMRead(u32 addr) override; + void SRAMWrite(u32 addr, u8 val) override; + + [[nodiscard]] const u8* GetROM() const override { return ROM.get(); } + [[nodiscard]] u32 GetROMLength() const override { return ROMLength; } + + u8* GetSaveMemory() const override; + u32 GetSaveMemoryLength() const override; + void SetSaveMemory(const u8* savedata, u32 savelen) override; +protected: + virtual void ProcessGPIO(); + + u8 SRAMRead_EEPROM(u32 addr); + void SRAMWrite_EEPROM(u32 addr, u8 val); + u8 SRAMRead_FLASH(u32 addr); + void SRAMWrite_FLASH(u32 addr, u8 val); + u8 SRAMRead_SRAM(u32 addr); + void SRAMWrite_SRAM(u32 addr, u8 val); + + std::unique_ptr ROM; + u32 ROMLength; + + struct + { + u16 data; + u16 direction; + u16 control; + + } GPIO {}; + + enum SaveType + { + S_NULL, + S_EEPROM4K, + S_EEPROM64K, + S_SRAM256K, + S_FLASH512K, + S_FLASH1M + }; + + // from DeSmuME + struct + { + u8 state; + u8 cmd; + u8 device; + u8 manufacturer; + u8 bank; + + } SRAMFlashState {}; + + std::unique_ptr SRAM = nullptr; + u32 SRAMLength = 0; + SaveType SRAMType = S_NULL; +private: + void SetupSave(u32 type); +}; + +// CartGameSolarSensor -- Boktai game cart +class CartGameSolarSensor : public CartGame +{ +public: + CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen); + CartGameSolarSensor(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen); + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + int SetInput(int num, bool pressed) override; + +protected: + void ProcessGPIO() override; + +private: + static const int kLuxLevels[11]; + + bool LightEdge = false; + u8 LightCounter = 0; + u8 LightSample = 0; + u8 LightLevel = 0; +}; + +// CartRAMExpansion -- RAM expansion cart (DS browser, ...) +class CartRAMExpansion : public CartCommon +{ +public: + CartRAMExpansion(); + ~CartRAMExpansion() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + void ROMWrite(u32 addr, u16 val) override; + +private: + u8 RAM[0x800000] {}; + u16 RAMEnable = 0; +}; + +// possible inputs for GBA carts that might accept user input +enum +{ + Input_SolarSensorDown = 0, + Input_SolarSensorUp, +}; + +class GBACartSlot +{ +public: + GBACartSlot(std::unique_ptr&& cart = nullptr) noexcept; + ~GBACartSlot() noexcept = default; + void Reset() noexcept; + void DoSavestate(Savestate* file) noexcept; + + /// Ejects the cart in the GBA slot (if any) + /// and inserts the given one. + /// + /// To insert a cart that does not require ROM data + /// (such as the RAM expansion pack), + /// create it manually with std::make_unique and pass it here. + /// + /// @param cart Movable \c unique_ptr to the GBA cart object. + /// May be \c nullptr, in which case the cart slot remains empty. + /// @post \c cart is \c nullptr and the underlying object + /// is moved into the cart slot. + void SetCart(std::unique_ptr&& cart) noexcept; + [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } + [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } + + void LoadAddon(int type) noexcept; + + /// @return The cart that was in the cart slot if any, + /// or \c nullptr if the cart slot was empty. + std::unique_ptr EjectCart() noexcept; + + // TODO: make more flexible, support nonbinary inputs + int SetInput(int num, bool pressed) noexcept; + + void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; } + + u16 ROMRead(u32 addr) const noexcept; + void ROMWrite(u32 addr, u16 val) noexcept; + + u8 SRAMRead(u32 addr) noexcept; + void SRAMWrite(u32 addr, u8 val) noexcept; + + /// This function is intended to allow frontends to save and load SRAM + /// without using melonDS APIs. + /// Modifying the emulated SRAM for any other reason is strongly discouraged. + /// The returned pointer may be invalidated if the emulator is reset, + /// or when a new game is loaded. + /// Consequently, don't store the returned pointer for any longer than necessary. + /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. + [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + + /// Sets the loaded cart's SRAM. + /// Does nothing if no cart is inserted + /// or the inserted cart doesn't support SRAM. + /// + /// @param savedata Buffer containing the raw contents of the SRAM. + /// The contents of this buffer are copied into the cart slot, + /// so the caller may dispose of it after this method returns. + /// @param savelen The length of the buffer in \c savedata, in bytes. + void SetSaveMemory(const u8* savedata, u32 savelen) noexcept; + + /// @returns The length of the buffer returned by ::GetSaveMemory() + /// if a cart is loaded and supports SRAM, otherwise zero. + [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } +private: + std::unique_ptr Cart = nullptr; + u16 OpenBusDecay = 0; +}; + +/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass +/// that can be inserted into the emulator or used to extract information about the cart beforehand. +/// @param romdata The ROM data to parse. +/// The returned cartridge will contain a copy of this data, +/// so the caller may deallocate \c romdata after this function returns. +/// @param romlen The length of the ROM data in bytes. +/// @returns A \c GBACart::CartCommon object representing the parsed ROM, +/// or \c nullptr if the ROM data couldn't be parsed. +std::unique_ptr ParseROM(const u8* romdata, u32 romlen); +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen); +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen); + +/// @param romdata The ROM data to parse. Will be moved-from. +/// @param romlen Length of romdata in bytes. +/// @param sramdata The save data to add to the cart. +/// May be \c nullptr, in which case the cart will have no save data. +/// @param sramlen Length of sramdata in bytes. +/// May be zero, in which case the cart will have no save data. +/// @return Unique pointer to the parsed GBA cart, +/// or \c nullptr if there was an error. +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::unique_ptr&& sramdata, u32 sramlen); + +} + +#endif // GBACART_H diff --git a/src/GPU.cpp b/src/GPU.cpp index 3b210c1d..07a6654e 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,79 +20,27 @@ #include "NDS.h" #include "GPU.h" -#ifdef JIT_ENABLED #include "ARMJIT.h" -#endif #include "GPU2D_Soft.h" #include "GPU3D.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace GPU -{ - #define LINE_CYCLES (355*6) #define HBLANK_CYCLES (48+(256*6)) #define FRAME_CYCLES (LINE_CYCLES * 263) -u16 VCount; -u32 NextVCount; -u16 TotalScanlines; +enum +{ + LCD_StartHBlank = 0, + LCD_StartScanline, + LCD_FinishFrame, +}; -bool RunFIFO; - -u16 DispStat[2], VMatch[2]; - -u8 Palette[2*1024]; -u8 OAM[2*1024]; - -u8 VRAM_A[128*1024]; -u8 VRAM_B[128*1024]; -u8 VRAM_C[128*1024]; -u8 VRAM_D[128*1024]; -u8 VRAM_E[ 64*1024]; -u8 VRAM_F[ 16*1024]; -u8 VRAM_G[ 16*1024]; -u8 VRAM_H[ 32*1024]; -u8 VRAM_I[ 16*1024]; -u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I}; -u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF}; - -u8 VRAMCNT[9]; -u8 VRAMSTAT; - -u32 VRAMMap_LCDC; - -u32 VRAMMap_ABG[0x20]; -u32 VRAMMap_AOBJ[0x10]; -u32 VRAMMap_BBG[0x8]; -u32 VRAMMap_BOBJ[0x8]; - -u32 VRAMMap_ABGExtPal[4]; -u32 VRAMMap_AOBJExtPal; -u32 VRAMMap_BBGExtPal[4]; -u32 VRAMMap_BOBJExtPal; - -u32 VRAMMap_Texture[4]; -u32 VRAMMap_TexPal[8]; - -u32 VRAMMap_ARM7[2]; - -u8* VRAMPtr_ABG[0x20]; -u8* VRAMPtr_AOBJ[0x10]; -u8* VRAMPtr_BBG[0x8]; -u8* VRAMPtr_BOBJ[0x8]; - -int FrontBuffer; -u32* Framebuffer[2][2]; -int Renderer = 0; - -GPU2D::Unit GPU2D_A(0); -GPU2D::Unit GPU2D_B(1); - -std::unique_ptr GPU2D_Renderer = {}; /* VRAM invalidation tracking @@ -115,75 +63,32 @@ std::unique_ptr GPU2D_Renderer = {}; VRAMDirty need to be reset for the respective VRAM bank. */ -VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG; -VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ; -VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG; -VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ; - -VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal; -VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal; -VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal; -VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal; - -VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture; -VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal; - -NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9]; - -u8 VRAMFlat_ABG[512*1024]; -u8 VRAMFlat_BBG[128*1024]; -u8 VRAMFlat_AOBJ[256*1024]; -u8 VRAMFlat_BOBJ[128*1024]; - -u8 VRAMFlat_ABGExtPal[32*1024]; -u8 VRAMFlat_BBGExtPal[32*1024]; -u8 VRAMFlat_AOBJExtPal[8*1024]; -u8 VRAMFlat_BOBJExtPal[8*1024]; - -u8 VRAMFlat_Texture[512*1024]; -u8 VRAMFlat_TexPal[128*1024]; - -u32 OAMDirty; -u32 PaletteDirty; - -#ifdef OGLRENDERER_ENABLED -std::unique_ptr CurGLCompositor = {}; -#endif - -bool Init() +GPU::GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d, std::unique_ptr&& renderer2d) noexcept : + NDS(nds), + GPU2D_A(0, *this), + GPU2D_B(1, *this), + GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique()), + GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique(*this)) { - GPU2D_Renderer = std::make_unique(); - if (!GPU3D::Init()) return false; + NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); + NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); + NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); + NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); - FrontBuffer = 0; - Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL; - Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL; - Renderer = 0; - - return true; + InitFramebuffers(); } -void DeInit() +GPU::~GPU() noexcept { - GPU2D_Renderer.reset(); - GPU3D::DeInit(); + // All unique_ptr fields are automatically cleaned up - if (Framebuffer[0][0]) delete[] Framebuffer[0][0]; - if (Framebuffer[0][1]) delete[] Framebuffer[0][1]; - if (Framebuffer[1][0]) delete[] Framebuffer[1][0]; - if (Framebuffer[1][1]) delete[] Framebuffer[1][1]; - - Framebuffer[0][0] = nullptr; - Framebuffer[0][1] = nullptr; - Framebuffer[1][0] = nullptr; - Framebuffer[1][1] = nullptr; - -#ifdef OGLRENDERER_ENABLED - CurGLCompositor = nullptr; -#endif + NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank); + NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline); + NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame); + NDS.UnregisterEventFunc(Event_DisplayFIFO, 0); } -void ResetVRAMCache() +void GPU::ResetVRAMCache() noexcept { for (int i = 0; i < 9; i++) VRAMDirty[i] = NonStupidBitField<128*1024/VRAMDirtyGranularity>(); @@ -211,7 +116,7 @@ void ResetVRAMCache() memset(VRAMFlat_TexPal, 0, sizeof(VRAMFlat_TexPal)); } -void Reset() +void GPU::Reset() noexcept { VCount = 0; NextVCount = -1; @@ -262,7 +167,7 @@ void Reset() memset(VRAMPtr_BOBJ, 0, sizeof(VRAMPtr_BOBJ)); size_t fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; @@ -280,12 +185,10 @@ void Reset() GPU2D_A.Reset(); GPU2D_B.Reset(); - GPU3D::Reset(); + GPU3D.Reset(); int backbuf = FrontBuffer ? 0 : 1; - GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); - - ResetRenderer(); + GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get()); ResetVRAMCache(); @@ -293,28 +196,23 @@ void Reset() PaletteDirty = 0xF; } -void Stop() +void GPU::Stop() noexcept { int fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; - memset(Framebuffer[0][0], 0, fbsize*4); - memset(Framebuffer[0][1], 0, fbsize*4); - memset(Framebuffer[1][0], 0, fbsize*4); - memset(Framebuffer[1][1], 0, fbsize*4); + memset(Framebuffer[0][0].get(), 0, fbsize*4); + memset(Framebuffer[0][1].get(), 0, fbsize*4); + memset(Framebuffer[1][0].get(), 0, fbsize*4); + memset(Framebuffer[1][1].get(), 0, fbsize*4); -#ifdef OGLRENDERER_ENABLED - // This needs a better way to know that we're - // using the OpenGL renderer specifically - if (GPU3D::CurrentRenderer->Accelerated) - CurGLCompositor->Stop(); -#endif + GPU3D.Stop(*this); } -void DoSavestate(Savestate* file) +void GPU::DoSavestate(Savestate* file) noexcept { file->Section("GPUG"); @@ -375,126 +273,54 @@ void DoSavestate(Savestate* file) GPU2D_A.DoSavestate(file); GPU2D_B.DoSavestate(file); - GPU3D::DoSavestate(file); + GPU3D.DoSavestate(file); - ResetVRAMCache(); + if (!file->Saving) + ResetVRAMCache(); } -void AssignFramebuffers() +void GPU::AssignFramebuffers() noexcept { int backbuf = FrontBuffer ? 0 : 1; - if (NDS::PowerControl9 & (1<<15)) + if (NDS.PowerControl9 & (1<<15)) { - GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0], Framebuffer[backbuf][1]); + GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0].get(), Framebuffer[backbuf][1].get()); } else { - GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); + GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get()); } } -void InitRenderer(int renderer) +void GPU::SetRenderer3D(std::unique_ptr&& renderer) noexcept { -#ifdef OGLRENDERER_ENABLED - if (renderer != renderer3D_Software) - { - CurGLCompositor = GLCompositor::New(); - // Create opengl renderer - if (!CurGLCompositor) - { - // Fallback on software renderer - renderer = 0; - GPU3D::CurrentRenderer = std::make_unique(); - } - if (renderer == renderer3D_OpenGL) - GPU3D::CurrentRenderer = GPU3D::GLRenderer::New(); - else if (renderer == renderer3D_OpenGLCompute) - GPU3D::CurrentRenderer = GPU3D::ComputeRenderer::New(); - if (!GPU3D::CurrentRenderer) - { - // Fallback on software renderer - CurGLCompositor.reset(); - renderer = 0; - GPU3D::CurrentRenderer = std::make_unique(); - } - } + if (renderer == nullptr) + GPU3D.SetCurrentRenderer(std::make_unique()); else -#endif - { - GPU3D::CurrentRenderer = std::make_unique(); - } + GPU3D.SetCurrentRenderer(std::move(renderer)); - Renderer = renderer; + InitFramebuffers(); } -void DeInitRenderer() +void GPU::InitFramebuffers() noexcept { - // Delete the 3D renderer, if it exists - GPU3D::CurrentRenderer.reset(); - -#ifdef OGLRENDERER_ENABLED - // Delete the compositor, if one exists - CurGLCompositor.reset(); -#endif -} - -void ResetRenderer() -{ - if (Renderer == 0) - { - GPU3D::CurrentRenderer->Reset(); - } -#ifdef OGLRENDERER_ENABLED - else - { - CurGLCompositor->Reset(); - GPU3D::CurrentRenderer->Reset(); - } -#endif -} - -void SetRenderSettings(int renderer, RenderSettings& settings) -{ - if (renderer != Renderer) - { - DeInitRenderer(); - InitRenderer(renderer); - } - int fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; - if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; } - if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; } - if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; } - if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; } + Framebuffer[0][0] = std::make_unique(fbsize); + Framebuffer[1][0] = std::make_unique(fbsize); + Framebuffer[0][1] = std::make_unique(fbsize); + Framebuffer[1][1] = std::make_unique(fbsize); - Framebuffer[0][0] = new u32[fbsize]; - Framebuffer[1][0] = new u32[fbsize]; - Framebuffer[0][1] = new u32[fbsize]; - Framebuffer[1][1] = new u32[fbsize]; - - memset(Framebuffer[0][0], 0, fbsize*4); - memset(Framebuffer[1][0], 0, fbsize*4); - memset(Framebuffer[0][1], 0, fbsize*4); - memset(Framebuffer[1][1], 0, fbsize*4); + memset(Framebuffer[0][0].get(), 0, fbsize*4); + memset(Framebuffer[1][0].get(), 0, fbsize*4); + memset(Framebuffer[0][1].get(), 0, fbsize*4); + memset(Framebuffer[1][1].get(), 0, fbsize*4); AssignFramebuffers(); - - if (Renderer == 0) - { - GPU3D::CurrentRenderer->SetRenderSettings(settings); - } -#ifdef OGLRENDERER_ENABLED - else - { - CurGLCompositor->SetRenderSettings(settings); - GPU3D::CurrentRenderer->SetRenderSettings(settings); - } -#endif } @@ -528,7 +354,14 @@ void SetRenderSettings(int renderer, RenderSettings& settings) // when reading: values are read from each bank and ORed together // when writing: value is written to each bank -u8* GetUniqueBankPtr(u32 mask, u32 offset) +u8* GPU::GetUniqueBankPtr(u32 mask, u32 offset) noexcept +{ + if (!mask || (mask & (mask - 1)) != 0) return NULL; + int num = __builtin_ctz(mask); + return &VRAM[num][offset & VRAMMask[num]]; +} + +const u8* GPU::GetUniqueBankPtr(u32 mask, u32 offset) const noexcept { if (!mask || (mask & (mask - 1)) != 0) return NULL; int num = __builtin_ctz(mask); @@ -543,7 +376,7 @@ u8* GetUniqueBankPtr(u32 mask, u32 offset) #define UNMAP_RANGE_PTR(map, base, n) \ for (int i = 0; i < n; i++) { VRAMMap_##map[(base)+i] &= ~bankmask; VRAMPtr_##map[(base)+i] = GetUniqueBankPtr(VRAMMap_##map[(base)+i], ((base)+i)<<14); } -void MapVRAM_AB(u32 bank, u8 cnt) +void GPU::MapVRAM_AB(u32 bank, u8 cnt) noexcept { cnt &= 0x9B; @@ -603,7 +436,7 @@ void MapVRAM_AB(u32 bank, u8 cnt) } } -void MapVRAM_CD(u32 bank, u8 cnt) +void GPU::MapVRAM_CD(u32 bank, u8 cnt) noexcept { cnt &= 0x9F; @@ -669,9 +502,7 @@ void MapVRAM_CD(u32 bank, u8 cnt) VRAMMap_ARM7[ofs] |= bankmask; memset(VRAMDirty[bank].Data, 0xFF, sizeof(VRAMDirty[bank].Data)); VRAMSTAT |= (1 << (bank-2)); -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidateWVRAM(ofs); -#endif + NDS.JIT.CheckAndInvalidateWVRAM(ofs); break; case 3: // texture @@ -692,7 +523,7 @@ void MapVRAM_CD(u32 bank, u8 cnt) } } -void MapVRAM_E(u32 bank, u8 cnt) +void GPU::MapVRAM_E(u32 bank, u8 cnt) noexcept { cnt &= 0x87; @@ -756,7 +587,7 @@ void MapVRAM_E(u32 bank, u8 cnt) } } -void MapVRAM_FG(u32 bank, u8 cnt) +void GPU::MapVRAM_FG(u32 bank, u8 cnt) noexcept { cnt &= 0x9F; @@ -856,7 +687,7 @@ void MapVRAM_FG(u32 bank, u8 cnt) } } -void MapVRAM_H(u32 bank, u8 cnt) +void GPU::MapVRAM_H(u32 bank, u8 cnt) noexcept { cnt &= 0x83; @@ -918,7 +749,7 @@ void MapVRAM_H(u32 bank, u8 cnt) } } -void MapVRAM_I(u32 bank, u8 cnt) +void GPU::MapVRAM_I(u32 bank, u8 cnt) noexcept { cnt &= 0x83; @@ -989,7 +820,7 @@ void MapVRAM_I(u32 bank, u8 cnt) } -void SetPowerCnt(u32 val) +void GPU::SetPowerCnt(u32 val) noexcept { // POWCNT1 effects: // * bit0: asplodes hardware??? not tested. @@ -1003,13 +834,13 @@ void SetPowerCnt(u32 val) GPU2D_A.SetEnabled(val & (1<<1)); GPU2D_B.SetEnabled(val & (1<<9)); - GPU3D::SetEnabled(val & (1<<3), val & (1<<2)); + GPU3D.SetEnabled(val & (1<<3), val & (1<<2)); AssignFramebuffers(); } -void DisplayFIFO(u32 x) +void GPU::DisplayFIFO(u32 x) noexcept { // sample the FIFO // as this starts 16 cycles (~3 pixels) before display start, @@ -1025,25 +856,25 @@ void DisplayFIFO(u32 x) if (x < 256) { // transfer the next 8 pixels - NDS::CheckDMAs(0, 0x04); - NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 6*8, DisplayFIFO, x+8); + NDS.CheckDMAs(0, 0x04); + NDS.ScheduleEvent(Event_DisplayFIFO, true, 6*8, 0, x+8); } else GPU2D_A.SampleFIFO(253, 3); // sample the remaining pixels } -void StartFrame() +void GPU::StartFrame() noexcept { // only run the display FIFO if needed: // * if it is used for display or capture // * if we have display FIFO DMA - RunFIFO = GPU2D_A.UsesFIFO() || NDS::DMAsInMode(0, 0x04); + RunFIFO = GPU2D_A.UsesFIFO() || NDS.DMAsInMode(0, 0x04); TotalScanlines = 0; StartScanline(0); } -void StartHBlank(u32 line) +void GPU::StartHBlank(u32 line) noexcept { DispStat[0] |= (1<<1); DispStat[1] |= (1<<1); @@ -1065,11 +896,11 @@ void StartHBlank(u32 line) GPU2D_Renderer->DrawSprites(line+1, &GPU2D_B); } - NDS::CheckDMAs(0, 0x02); + NDS.CheckDMAs(0, 0x02); } else if (VCount == 215) { - GPU3D::VCount215(); + GPU3D.VCount215(*this); } else if (VCount == 262) { @@ -1077,30 +908,48 @@ void StartHBlank(u32 line) GPU2D_Renderer->DrawSprites(0, &GPU2D_B); } - if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank); - if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank); + if (DispStat[0] & (1<<4)) NDS.SetIRQ(0, IRQ_HBlank); + if (DispStat[1] & (1<<4)) NDS.SetIRQ(1, IRQ_HBlank); if (VCount < 262) - NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), StartScanline, line+1); + NDS.ScheduleEvent(Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_StartScanline, line+1); else - NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), FinishFrame, line+1); + NDS.ScheduleEvent(Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_FinishFrame, line+1); } -void FinishFrame(u32 lines) +void GPU::FinishFrame(u32 lines) noexcept { FrontBuffer = FrontBuffer ? 0 : 1; AssignFramebuffers(); TotalScanlines = lines; - if (GPU3D::AbortFrame) + if (GPU3D.AbortFrame) { - GPU3D::RestartFrame(); - GPU3D::AbortFrame = false; + GPU3D.RestartFrame(*this); + GPU3D.AbortFrame = false; } } -void StartScanline(u32 line) +void GPU::BlankFrame() noexcept +{ + int backbuf = FrontBuffer ? 0 : 1; + int fbsize; + if (GPU3D.IsRendererAccelerated()) + fbsize = (256*3 + 1) * 192; + else + fbsize = 256 * 192; + + memset(Framebuffer[backbuf][0].get(), 0, fbsize*4); + memset(Framebuffer[backbuf][1].get(), 0, fbsize*4); + + FrontBuffer = backbuf; + AssignFramebuffers(); + + TotalScanlines = 263; +} + +void GPU::StartScanline(u32 line) noexcept { if (line == 0) VCount = 0; @@ -1118,7 +967,7 @@ void StartScanline(u32 line) { DispStat[0] |= (1<<2); - if (DispStat[0] & (1<<5)) NDS::SetIRQ(0, NDS::IRQ_VCount); + if (DispStat[0] & (1<<5)) NDS.SetIRQ(0, IRQ_VCount); } else DispStat[0] &= ~(1<<2); @@ -1127,7 +976,7 @@ void StartScanline(u32 line) { DispStat[1] |= (1<<2); - if (DispStat[1] & (1<<5)) NDS::SetIRQ(1, NDS::IRQ_VCount); + if (DispStat[1] & (1<<5)) NDS.SetIRQ(1, IRQ_VCount); } else DispStat[1] &= ~(1<<2); @@ -1136,9 +985,9 @@ void StartScanline(u32 line) GPU2D_B.CheckWindows(VCount); if (VCount >= 2 && VCount < 194) - NDS::CheckDMAs(0, 0x03); + NDS.CheckDMAs(0, 0x03); else if (VCount == 194) - NDS::StopDMAs(0, 0x03); + NDS.StopDMAs(0, 0x03); if (line < 192) { @@ -1150,7 +999,7 @@ void StartScanline(u32 line) } if (RunFIFO) - NDS::ScheduleEvent(NDS::Event_DisplayFIFO, false, 32, DisplayFIFO, 0); + NDS.ScheduleEvent(Event_DisplayFIFO, false, 32, 0, 0); } if (VCount == 262) @@ -1170,37 +1019,35 @@ void StartScanline(u32 line) // texture memory anyway and only update it before the start //of the next frame. // So we can give the rasteriser a bit more headroom - GPU3D::VCount144(); + GPU3D.VCount144(*this); // VBlank DispStat[0] |= (1<<0); DispStat[1] |= (1<<0); - NDS::StopDMAs(0, 0x04); + NDS.StopDMAs(0, 0x04); - NDS::CheckDMAs(0, 0x01); - NDS::CheckDMAs(1, 0x11); + NDS.CheckDMAs(0, 0x01); + NDS.CheckDMAs(1, 0x11); - if (DispStat[0] & (1<<3)) NDS::SetIRQ(0, NDS::IRQ_VBlank); - if (DispStat[1] & (1<<3)) NDS::SetIRQ(1, NDS::IRQ_VBlank); + if (DispStat[0] & (1<<3)) NDS.SetIRQ(0, IRQ_VBlank); + if (DispStat[1] & (1<<3)) NDS.SetIRQ(1, IRQ_VBlank); GPU2D_A.VBlank(); GPU2D_B.VBlank(); - GPU3D::VBlank(); + GPU3D.VBlank(); -#ifdef OGLRENDERER_ENABLED // Need a better way to identify the openGL renderer in particular - if (GPU3D::CurrentRenderer->Accelerated) - CurGLCompositor->RenderFrame(); -#endif + if (GPU3D.IsRendererAccelerated()) + GPU3D.Blit(*this); } } - NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line); + NDS.ScheduleEvent(Event_LCD, true, HBLANK_CYCLES, LCD_StartHBlank, line); } -void SetDispStat(u32 cpu, u16 val) +void GPU::SetDispStat(u32 cpu, u16 val) noexcept { val &= 0xFFB8; DispStat[cpu] &= 0x0047; @@ -1209,7 +1056,7 @@ void SetDispStat(u32 cpu, u16 val) VMatch[cpu] = (val >> 8) | ((val & 0x80) << 1); } -void SetVCount(u16 val) +void GPU::SetVCount(u16 val) noexcept { // VCount write is delayed until the next scanline @@ -1217,12 +1064,12 @@ void SetVCount(u16 val) // 3D engine seems to give up on the current frame in that situation, repeating the last two scanlines // TODO: also check the various DMA types that can be involved - GPU3D::AbortFrame |= NextVCount != val; + GPU3D.AbortFrame |= NextVCount != val; NextVCount = val; } template -NonStupidBitField VRAMTrackingSet::DeriveState(u32* currentMappings) +NonStupidBitField VRAMTrackingSet::DeriveState(const u32* currentMappings, GPU& gpu) { NonStupidBitField result; u16 banksToBeZeroed = 0; @@ -1251,20 +1098,20 @@ NonStupidBitField VRAMTrackingSet> 14)]; + u32 dirty = ((u32*)gpu.VRAMDirty[num].Data)[i & (gpu.VRAMMask[num] >> 14)]; result.Data[i / 2] |= (u64)dirty << ((i&1)*32); } else if (MappingGranularity == 8*1024) { - u16 dirty = ((u16*)VRAMDirty[num].Data)[i & (VRAMMask[num] >> 13)]; + u16 dirty = ((u16*)gpu.VRAMDirty[num].Data)[i & (gpu.VRAMMask[num] >> 13)]; result.Data[i / 4] |= (u64)dirty << ((i&3)*16); } else if (MappingGranularity == 128*1024) { - result.Data[i * 4 + 0] |= VRAMDirty[num].Data[0]; - result.Data[i * 4 + 1] |= VRAMDirty[num].Data[1]; - result.Data[i * 4 + 2] |= VRAMDirty[num].Data[2]; - result.Data[i * 4 + 3] |= VRAMDirty[num].Data[3]; + result.Data[i * 4 + 0] |= gpu.VRAMDirty[num].Data[0]; + result.Data[i * 4 + 1] |= gpu.VRAMDirty[num].Data[1]; + result.Data[i * 4 + 2] |= gpu.VRAMDirty[num].Data[2]; + result.Data[i * 4 + 3] |= gpu.VRAMDirty[num].Data[3]; } else { @@ -1279,137 +1126,63 @@ NonStupidBitField VRAMTrackingSet VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*); -template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*); -template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*); -template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*); -template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*); -template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*); +template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(const u32*, GPU& gpu); -template -inline bool CopyLinearVRAM(u8* flat, u32* mappings, NonStupidBitField& dirty, u64 (*slowAccess)(u32 addr)) + + +bool GPU::MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept { - const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity; - - bool change = false; - - typename NonStupidBitField::Iterator it = dirty.Begin(); - while (it != dirty.End()) - { - u32 offset = *it * VRAMDirtyGranularity; - u8* dst = flat + offset; - u8* fastAccess = GetUniqueBankPtr(mappings[*it / VRAMBitsPerMapping], offset); - if (fastAccess) - { - memcpy(dst, fastAccess, VRAMDirtyGranularity); - } - else - { - for (u32 i = 0; i < VRAMDirtyGranularity; i += 8) - *(u64*)&dst[i] = slowAccess(offset + i); - } - change = true; - it++; - } - return change; + return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, &GPU::ReadVRAM_Texture); } - -bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) +bool GPU::MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, ReadVRAM_Texture); + return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, &GPU::ReadVRAM_TexPal); } -bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) + +bool GPU::MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, ReadVRAM_TexPal); + return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, &GPU::ReadVRAM_ABG); } - -bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) +bool GPU::MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, ReadVRAM_ABG); + return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, &GPU::ReadVRAM_BBG); } -bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) + +bool GPU::MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, ReadVRAM_BBG); + return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, &GPU::ReadVRAM_AOBJ); } - -bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) +bool GPU::MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, ReadVRAM_AOBJ); + return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, &GPU::ReadVRAM_BOBJ); } -bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) + +bool GPU::MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, ReadVRAM_BOBJ); + return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, &GPU::ReadVRAM_ABGExtPal); } - -template -T ReadVRAM_ABGExtPal(u32 addr) +bool GPU::MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept { - u32 mask = VRAMMap_ABGExtPal[(addr >> 13) & 0x3]; - - T ret = 0; - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0x7FFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; + return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, &GPU::ReadVRAM_BBGExtPal); } -template -T ReadVRAM_BBGExtPal(u32 addr) +bool GPU::MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept { - u32 mask = VRAMMap_BBGExtPal[(addr >> 13) & 0x3]; - - T ret = 0; - if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; - - return ret; + return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, &GPU::ReadVRAM_AOBJExtPal); } - -template -T ReadVRAM_AOBJExtPal(u32 addr) +bool GPU::MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept { - u32 mask = VRAMMap_AOBJExtPal; - - T ret = 0; - if (mask & (1<<4)) ret |= *(T*)&VRAM_F[addr & 0x1FFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_G[addr & 0x1FFF]; - - return ret; + return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, &GPU::ReadVRAM_BOBJExtPal); } - -template -T ReadVRAM_BOBJExtPal(u32 addr) -{ - u32 mask = VRAMMap_BOBJExtPal; - - T ret = 0; - if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x1FFF]; - - return ret; -} - -bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, ReadVRAM_ABGExtPal); -} -bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, ReadVRAM_BBGExtPal); -} - -bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, ReadVRAM_AOBJExtPal); -} -bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, ReadVRAM_BOBJExtPal); -} - } diff --git a/src/GPU.h b/src/GPU.h index ee75fc19..780d5e01 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,67 +22,16 @@ #include #include "GPU2D.h" +#include "GPU3D.h" #include "NonStupidBitfield.h" -#ifdef OGLRENDERER_ENABLED -#include "GPU_OpenGL.h" -#endif - -namespace GPU +namespace melonDS { +class GPU3D; +class ARMJIT; -extern u16 VCount; -extern u16 TotalScanlines; - -extern u16 DispStat[2]; - -extern u8 VRAMCNT[9]; -extern u8 VRAMSTAT; - -extern u8 Palette[2*1024]; -extern u8 OAM[2*1024]; - -extern u8 VRAM_A[128*1024]; -extern u8 VRAM_B[128*1024]; -extern u8 VRAM_C[128*1024]; -extern u8 VRAM_D[128*1024]; -extern u8 VRAM_E[ 64*1024]; -extern u8 VRAM_F[ 16*1024]; -extern u8 VRAM_G[ 16*1024]; -extern u8 VRAM_H[ 32*1024]; -extern u8 VRAM_I[ 16*1024]; - -extern u8* const VRAM[9]; - -extern u32 VRAMMap_LCDC; -extern u32 VRAMMap_ABG[0x20]; -extern u32 VRAMMap_AOBJ[0x10]; -extern u32 VRAMMap_BBG[0x8]; -extern u32 VRAMMap_BOBJ[0x8]; -extern u32 VRAMMap_ABGExtPal[4]; -extern u32 VRAMMap_AOBJExtPal; -extern u32 VRAMMap_BBGExtPal[4]; -extern u32 VRAMMap_BOBJExtPal; -extern u32 VRAMMap_Texture[4]; -extern u32 VRAMMap_TexPal[8]; -extern u32 VRAMMap_ARM7[2]; - -extern u8* VRAMPtr_ABG[0x20]; -extern u8* VRAMPtr_AOBJ[0x10]; -extern u8* VRAMPtr_BBG[0x8]; -extern u8* VRAMPtr_BOBJ[0x8]; - -extern int FrontBuffer; -extern u32* Framebuffer[2][2]; - -extern GPU2D::Unit GPU2D_A; -extern GPU2D::Unit GPU2D_B; - -extern int Renderer; - -const u32 VRAMDirtyGranularity = 512; - -extern NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9]; +static constexpr u32 VRAMDirtyGranularity = 512; +class GPU; template struct VRAMTrackingSet @@ -100,532 +49,651 @@ struct VRAMTrackingSet Mapping[i] = 0x8000; } } - NonStupidBitField DeriveState(u32* currentMappings); + NonStupidBitField DeriveState(const u32* currentMappings, GPU& gpu); }; -extern VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG; -extern VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ; -extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG; -extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ; - -extern VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal; -extern VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal; -extern VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal; -extern VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal; - -extern VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture; -extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal; - -extern u8 VRAMFlat_ABG[512*1024]; -extern u8 VRAMFlat_BBG[128*1024]; -extern u8 VRAMFlat_AOBJ[256*1024]; -extern u8 VRAMFlat_BOBJ[128*1024]; - -extern u8 VRAMFlat_ABGExtPal[32*1024]; -extern u8 VRAMFlat_BBGExtPal[32*1024]; - -extern u8 VRAMFlat_AOBJExtPal[8*1024]; -extern u8 VRAMFlat_BOBJExtPal[8*1024]; - -extern u8 VRAMFlat_Texture[512*1024]; -extern u8 VRAMFlat_TexPal[128*1024]; - -bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty); - -void SyncDirtyFlags(); - -extern u32 OAMDirty; -extern u32 PaletteDirty; - -#ifdef OGLRENDERER_ENABLED -extern std::unique_ptr CurGLCompositor; -#endif - -struct RenderSettings +class GPU { - bool Soft_Threaded; +public: + explicit GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d = nullptr, std::unique_ptr&& renderer2d = nullptr) noexcept; + ~GPU() noexcept; + void Reset() noexcept; + void Stop() noexcept; - int GL_ScaleFactor; - bool GL_BetterPolygons; - bool GL_HiresCoordinates; + void DoSavestate(Savestate* file) noexcept; + + /// Sets the active renderer to the renderer given in the provided pointer. + /// The pointer is moved-from, so it will be \c nullptr after this method is called. + /// If the pointer is \c nullptr, the renderer is reset to the default renderer. + void SetRenderer3D(std::unique_ptr&& renderer) noexcept; + [[nodiscard]] const Renderer3D& GetRenderer3D() const noexcept { return GPU3D.GetCurrentRenderer(); } + [[nodiscard]] Renderer3D& GetRenderer3D() noexcept { return GPU3D.GetCurrentRenderer(); } + + u8* GetUniqueBankPtr(u32 mask, u32 offset) noexcept; + const u8* GetUniqueBankPtr(u32 mask, u32 offset) const noexcept; + + void SetRenderer2D(std::unique_ptr&& renderer) noexcept { GPU2D_Renderer = std::move(renderer); } + [[nodiscard]] const GPU2D::Renderer2D& GetRenderer2D() const noexcept { return *GPU2D_Renderer; } + [[nodiscard]] GPU2D::Renderer2D& GetRenderer2D() noexcept { return *GPU2D_Renderer; } + + void MapVRAM_AB(u32 bank, u8 cnt) noexcept; + void MapVRAM_CD(u32 bank, u8 cnt) noexcept; + void MapVRAM_E(u32 bank, u8 cnt) noexcept; + void MapVRAM_FG(u32 bank, u8 cnt) noexcept; + void MapVRAM_H(u32 bank, u8 cnt) noexcept; + void MapVRAM_I(u32 bank, u8 cnt) noexcept; + + template + T ReadVRAM_LCDC(u32 addr) const noexcept + { + int bank; + + switch (addr & 0xFF8FC000) + { + case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: + case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: + bank = 0; + addr &= 0x1FFFF; + break; + + case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: + case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: + bank = 1; + addr &= 0x1FFFF; + break; + + case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: + case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: + bank = 2; + addr &= 0x1FFFF; + break; + + case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: + case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: + bank = 3; + addr &= 0x1FFFF; + break; + + case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: + bank = 4; + addr &= 0xFFFF; + break; + + case 0x06890000: + bank = 5; + addr &= 0x3FFF; + break; + + case 0x06894000: + bank = 6; + addr &= 0x3FFF; + break; + + case 0x06898000: + case 0x0689C000: + bank = 7; + addr &= 0x7FFF; + break; + + case 0x068A0000: + bank = 8; + addr &= 0x3FFF; + break; + + default: return 0; + } + + if (VRAMMap_LCDC & (1< + void WriteVRAM_LCDC(u32 addr, T val) + { + int bank; + + switch (addr & 0xFF8FC000) + { + case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: + case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: + bank = 0; + addr &= 0x1FFFF; + break; + + case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: + case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: + bank = 1; + addr &= 0x1FFFF; + break; + + case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: + case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: + bank = 2; + addr &= 0x1FFFF; + break; + + case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: + case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: + bank = 3; + addr &= 0x1FFFF; + break; + + case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: + bank = 4; + addr &= 0xFFFF; + break; + + case 0x06890000: + bank = 5; + addr &= 0x3FFF; + break; + + case 0x06894000: + bank = 6; + addr &= 0x3FFF; + break; + + case 0x06898000: + case 0x0689C000: + bank = 7; + addr &= 0x7FFF; + break; + + case 0x068A0000: + bank = 8; + addr &= 0x3FFF; + break; + + default: return; + } + + if (VRAMMap_LCDC & (1< + T ReadVRAM_ABG(u32 addr) const noexcept + { + u8* ptr = VRAMPtr_ABG[(addr >> 14) & 0x1F]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; + + if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; + if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; + } + + template + void WriteVRAM_ABG(u32 addr, T val) + { + u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; + + if (mask & (1<<0)) + { + VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_A[addr & 0x1FFFF] = val; + } + if (mask & (1<<1)) + { + VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_B[addr & 0x1FFFF] = val; + } + if (mask & (1<<2)) + { + VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_C[addr & 0x1FFFF] = val; + } + if (mask & (1<<3)) + { + VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_D[addr & 0x1FFFF] = val; + } + if (mask & (1<<4)) + { + VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_E[addr & 0xFFFF] = val; + } + if (mask & (1<<5)) + { + VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_F[addr & 0x3FFF] = val; + } + if (mask & (1<<6)) + { + VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_G[addr & 0x3FFF] = val; + } + } + + + template + T ReadVRAM_AOBJ(u32 addr) const noexcept + { + u8* ptr = VRAMPtr_AOBJ[(addr >> 14) & 0xF]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; + + if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; + if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; + } + + template + void WriteVRAM_AOBJ(u32 addr, T val) + { + u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; + + if (mask & (1<<0)) + { + VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_A[addr & 0x1FFFF] = val; + } + if (mask & (1<<1)) + { + VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_B[addr & 0x1FFFF] = val; + } + if (mask & (1<<4)) + { + VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_E[addr & 0xFFFF] = val; + } + if (mask & (1<<5)) + { + VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_F[addr & 0x3FFF] = val; + } + if (mask & (1<<6)) + { + VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_G[addr & 0x3FFF] = val; + } + } + + + template + T ReadVRAM_BBG(u32 addr) const noexcept + { + u8* ptr = VRAMPtr_BBG[(addr >> 14) & 0x7]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; + + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; + if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; + + return ret; + } + + template + void WriteVRAM_BBG(u32 addr, T val) + { + u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; + + if (mask & (1<<2)) + { + VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_C[addr & 0x1FFFF] = val; + } + if (mask & (1<<7)) + { + VRAMDirty[7][(addr & 0x7FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_H[addr & 0x7FFF] = val; + } + if (mask & (1<<8)) + { + VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_I[addr & 0x3FFF] = val; + } + } + + + template + T ReadVRAM_BOBJ(u32 addr) const noexcept + { + u8* ptr = VRAMPtr_BOBJ[(addr >> 14) & 0x7]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; + + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; + + return ret; + } + + template + void WriteVRAM_BOBJ(u32 addr, T val) + { + u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; + + if (mask & (1<<3)) + { + VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_D[addr & 0x1FFFF] = val; + } + if (mask & (1<<8)) + { + VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_I[addr & 0x3FFF] = val; + } + } + + template + T ReadVRAM_ARM7(u32 addr) const noexcept + { + T ret = 0; + u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; + + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + + return ret; + } + + template + void WriteVRAM_ARM7(u32 addr, T val) + { + u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; + + if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val; + if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val; + } + + + template + T ReadVRAM_BG(u32 addr) const noexcept + { + if ((addr & 0xFFE00000) == 0x06000000) + return ReadVRAM_ABG(addr); + else + return ReadVRAM_BBG(addr); + } + + template + T ReadVRAM_OBJ(u32 addr) const noexcept + { + if ((addr & 0xFFE00000) == 0x06400000) + return ReadVRAM_AOBJ(addr); + else + return ReadVRAM_BOBJ(addr); + } + + + template + T ReadVRAM_Texture(u32 addr) const noexcept + { + T ret = 0; + u32 mask = VRAMMap_Texture[(addr >> 17) & 0x3]; + + if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; + if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + + return ret; + } + + template + T ReadVRAM_TexPal(u32 addr) const noexcept + { + T ret = 0; + u32 mask = VRAMMap_TexPal[(addr >> 14) & 0x7]; + + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; + } + + template + T ReadPalette(u32 addr) const noexcept + { + return *(T*)&Palette[addr & 0x7FF]; + } + + template + void WritePalette(u32 addr, T val) + { + addr &= 0x7FF; + + *(T*)&Palette[addr] = val; + PaletteDirty |= 1 << (addr / VRAMDirtyGranularity); + } + + template + T ReadOAM(u32 addr) const noexcept + { + return *(T*)&OAM[addr & 0x7FF]; + } + + template + void WriteOAM(u32 addr, T val) + { + addr &= 0x7FF; + + *(T*)&OAM[addr] = val; + OAMDirty |= 1 << (addr / 1024); + } + + void SetPowerCnt(u32 val) noexcept; + + void StartFrame() noexcept; + void FinishFrame(u32 lines) noexcept; + void BlankFrame() noexcept; + void StartScanline(u32 line) noexcept; + void StartHBlank(u32 line) noexcept; + + void DisplayFIFO(u32 x) noexcept; + + void SetDispStat(u32 cpu, u16 val) noexcept; + + void SetVCount(u16 val) noexcept; + bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept; + + void SyncDirtyFlags() noexcept; + + melonDS::NDS& NDS; + u16 VCount = 0; + u16 TotalScanlines = 0; + u16 DispStat[2] {}; + u8 VRAMCNT[9] {}; + u8 VRAMSTAT = 0; + + alignas(u64) u8 Palette[2*1024] {}; + alignas(u64) u8 OAM[2*1024] {}; + + alignas(u64) u8 VRAM_A[128*1024] {}; + alignas(u64) u8 VRAM_B[128*1024] {}; + alignas(u64) u8 VRAM_C[128*1024] {}; + alignas(u64) u8 VRAM_D[128*1024] {}; + alignas(u64) u8 VRAM_E[ 64*1024] {}; + alignas(u64) u8 VRAM_F[ 16*1024] {}; + alignas(u64) u8 VRAM_G[ 16*1024] {}; + alignas(u64) u8 VRAM_H[ 32*1024] {}; + alignas(u64) u8 VRAM_I[ 16*1024] {}; + + u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I}; + u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF}; + + u32 VRAMMap_LCDC = 0; + u32 VRAMMap_ABG[0x20] {}; + u32 VRAMMap_AOBJ[0x10] {}; + u32 VRAMMap_BBG[0x8] {}; + u32 VRAMMap_BOBJ[0x8] {}; + u32 VRAMMap_ABGExtPal[4] {}; + u32 VRAMMap_AOBJExtPal {}; + u32 VRAMMap_BBGExtPal[4] {}; + u32 VRAMMap_BOBJExtPal {}; + u32 VRAMMap_Texture[4] {}; + u32 VRAMMap_TexPal[8] {}; + u32 VRAMMap_ARM7[2] {}; + + u8* VRAMPtr_ABG[0x20] {}; + u8* VRAMPtr_AOBJ[0x10] {}; + u8* VRAMPtr_BBG[0x8] {}; + u8* VRAMPtr_BOBJ[0x8] {}; + + int FrontBuffer = 0; + std::unique_ptr Framebuffer[2][2] {}; + + GPU2D::Unit GPU2D_A; + GPU2D::Unit GPU2D_B; + melonDS::GPU3D GPU3D; + + NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9] {}; + VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG {}; + VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ {}; + VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG {}; + VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ {}; + + VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal {}; + VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal {}; + VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal {}; + VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal {}; + + VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture {}; + VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal {}; + + u8 VRAMFlat_ABG[512*1024] {}; + u8 VRAMFlat_BBG[128*1024] {}; + u8 VRAMFlat_AOBJ[256*1024] {}; + u8 VRAMFlat_BOBJ[128*1024] {}; + + alignas(u16) u8 VRAMFlat_ABGExtPal[32*1024] {}; + alignas(u16) u8 VRAMFlat_BBGExtPal[32*1024] {}; + + alignas(u16) u8 VRAMFlat_AOBJExtPal[8*1024] {}; + alignas(u16) u8 VRAMFlat_BOBJExtPal[8*1024] {}; + + alignas(u64) u8 VRAMFlat_Texture[512*1024] {}; + alignas(u64) u8 VRAMFlat_TexPal[128*1024] {}; +private: + void ResetVRAMCache() noexcept; + void AssignFramebuffers() noexcept; + void InitFramebuffers() noexcept; + template + T ReadVRAM_ABGExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_ABGExtPal[(addr >> 13) & 0x3]; + + T ret = 0; + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0x7FFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; + } + + template + T ReadVRAM_BBGExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_BBGExtPal[(addr >> 13) & 0x3]; + + T ret = 0; + if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; + + return ret; + } + + template + T ReadVRAM_AOBJExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_AOBJExtPal; + + T ret = 0; + if (mask & (1<<4)) ret |= *(T*)&VRAM_F[addr & 0x1FFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_G[addr & 0x1FFF]; + + return ret; + } + + template + T ReadVRAM_BOBJExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_BOBJExtPal; + + T ret = 0; + if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x1FFF]; + + return ret; + } + + template + constexpr bool CopyLinearVRAM(u8* flat, const u32* mappings, NonStupidBitField& dirty, u64 (GPU::* const slowAccess)(u32) const noexcept) noexcept + { + const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity; + + bool change = false; + + typename NonStupidBitField::Iterator it = dirty.Begin(); + while (it != dirty.End()) + { + u32 offset = *it * VRAMDirtyGranularity; + u8* dst = flat + offset; + u8* fastAccess = GetUniqueBankPtr(mappings[*it / VRAMBitsPerMapping], offset); + if (fastAccess) + { + memcpy(dst, fastAccess, VRAMDirtyGranularity); + } + else + { + for (u32 i = 0; i < VRAMDirtyGranularity; i += 8) + *(u64*)&dst[i] = (this->*slowAccess)(offset + i); + } + change = true; + it++; + } + return change; + } + + u32 NextVCount = 0; + + bool RunFIFO = false; + + u16 VMatch[2] {}; + + std::unique_ptr GPU2D_Renderer = nullptr; + + u32 OAMDirty = 0; + u32 PaletteDirty = 0; }; - - -bool Init(); -void DeInit(); -void Reset(); -void Stop(); - -void DoSavestate(Savestate* file); - -enum -{ - renderer3D_Software = 0, -#ifdef OGLRENDERER_ENABLED - renderer3D_OpenGL, - renderer3D_OpenGLCompute, -#endif - renderer3D_Max, -}; - -void InitRenderer(int renderer); -void DeInitRenderer(); -void ResetRenderer(); - -void SetRenderSettings(int renderer, RenderSettings& settings); - - -u8* GetUniqueBankPtr(u32 mask, u32 offset); - -void MapVRAM_AB(u32 bank, u8 cnt); -void MapVRAM_CD(u32 bank, u8 cnt); -void MapVRAM_E(u32 bank, u8 cnt); -void MapVRAM_FG(u32 bank, u8 cnt); -void MapVRAM_H(u32 bank, u8 cnt); -void MapVRAM_I(u32 bank, u8 cnt); - - -template -T ReadVRAM_LCDC(u32 addr) -{ - int bank; - - switch (addr & 0xFF8FC000) - { - case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: - case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: - bank = 0; - addr &= 0x1FFFF; - break; - - case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: - case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: - bank = 1; - addr &= 0x1FFFF; - break; - - case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: - case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: - bank = 2; - addr &= 0x1FFFF; - break; - - case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: - case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: - bank = 3; - addr &= 0x1FFFF; - break; - - case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: - bank = 4; - addr &= 0xFFFF; - break; - - case 0x06890000: - bank = 5; - addr &= 0x3FFF; - break; - - case 0x06894000: - bank = 6; - addr &= 0x3FFF; - break; - - case 0x06898000: - case 0x0689C000: - bank = 7; - addr &= 0x7FFF; - break; - - case 0x068A0000: - bank = 8; - addr &= 0x3FFF; - break; - - default: return 0; - } - - if (VRAMMap_LCDC & (1< -void WriteVRAM_LCDC(u32 addr, T val) -{ - int bank; - - switch (addr & 0xFF8FC000) - { - case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: - case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: - bank = 0; - addr &= 0x1FFFF; - break; - - case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: - case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: - bank = 1; - addr &= 0x1FFFF; - break; - - case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: - case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: - bank = 2; - addr &= 0x1FFFF; - break; - - case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: - case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: - bank = 3; - addr &= 0x1FFFF; - break; - - case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: - bank = 4; - addr &= 0xFFFF; - break; - - case 0x06890000: - bank = 5; - addr &= 0x3FFF; - break; - - case 0x06894000: - bank = 6; - addr &= 0x3FFF; - break; - - case 0x06898000: - case 0x0689C000: - bank = 7; - addr &= 0x7FFF; - break; - - case 0x068A0000: - bank = 8; - addr &= 0x3FFF; - break; - - default: return; - } - - if (VRAMMap_LCDC & (1< -T ReadVRAM_ABG(u32 addr) -{ - u8* ptr = VRAMPtr_ABG[(addr >> 14) & 0x1F]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; - - if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; - if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_ABG(u32 addr, T val) -{ - u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; - - if (mask & (1<<0)) - { - VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_A[addr & 0x1FFFF] = val; - } - if (mask & (1<<1)) - { - VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_B[addr & 0x1FFFF] = val; - } - if (mask & (1<<2)) - { - VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_C[addr & 0x1FFFF] = val; - } - if (mask & (1<<3)) - { - VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_D[addr & 0x1FFFF] = val; - } - if (mask & (1<<4)) - { - VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_E[addr & 0xFFFF] = val; - } - if (mask & (1<<5)) - { - VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_F[addr & 0x3FFF] = val; - } - if (mask & (1<<6)) - { - VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_G[addr & 0x3FFF] = val; - } -} - - -template -T ReadVRAM_AOBJ(u32 addr) -{ - u8* ptr = VRAMPtr_AOBJ[(addr >> 14) & 0xF]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; - - if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; - if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_AOBJ(u32 addr, T val) -{ - u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; - - if (mask & (1<<0)) - { - VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_A[addr & 0x1FFFF] = val; - } - if (mask & (1<<1)) - { - VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_B[addr & 0x1FFFF] = val; - } - if (mask & (1<<4)) - { - VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_E[addr & 0xFFFF] = val; - } - if (mask & (1<<5)) - { - VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_F[addr & 0x3FFF] = val; - } - if (mask & (1<<6)) - { - VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_G[addr & 0x3FFF] = val; - } -} - - -template -T ReadVRAM_BBG(u32 addr) -{ - u8* ptr = VRAMPtr_BBG[(addr >> 14) & 0x7]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; - - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; - if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_BBG(u32 addr, T val) -{ - u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; - - if (mask & (1<<2)) - { - VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_C[addr & 0x1FFFF] = val; - } - if (mask & (1<<7)) - { - VRAMDirty[7][(addr & 0x7FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_H[addr & 0x7FFF] = val; - } - if (mask & (1<<8)) - { - VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_I[addr & 0x3FFF] = val; - } -} - - -template -T ReadVRAM_BOBJ(u32 addr) -{ - u8* ptr = VRAMPtr_BOBJ[(addr >> 14) & 0x7]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; - - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_BOBJ(u32 addr, T val) -{ - u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; - - if (mask & (1<<3)) - { - VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_D[addr & 0x1FFFF] = val; - } - if (mask & (1<<8)) - { - VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_I[addr & 0x3FFF] = val; - } -} - -template -T ReadVRAM_ARM7(u32 addr) -{ - T ret = 0; - u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; - - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - - return ret; -} - -template -void WriteVRAM_ARM7(u32 addr, T val) -{ - u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; - - if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val; - if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val; -} - - -template -T ReadVRAM_BG(u32 addr) -{ - if ((addr & 0xFFE00000) == 0x06000000) - return ReadVRAM_ABG(addr); - else - return ReadVRAM_BBG(addr); -} - -template -T ReadVRAM_OBJ(u32 addr) -{ - if ((addr & 0xFFE00000) == 0x06400000) - return ReadVRAM_AOBJ(addr); - else - return ReadVRAM_BOBJ(addr); -} - - -template -T ReadVRAM_Texture(u32 addr) -{ - T ret = 0; - u32 mask = VRAMMap_Texture[(addr >> 17) & 0x3]; - - if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; - if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - - return ret; -} - -template -T ReadVRAM_TexPal(u32 addr) -{ - T ret = 0; - u32 mask = VRAMMap_TexPal[(addr >> 14) & 0x7]; - - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; -} - -template -T ReadPalette(u32 addr) -{ - return *(T*)&Palette[addr & 0x7FF]; -} - -template -void WritePalette(u32 addr, T val) -{ - addr &= 0x7FF; - - *(T*)&Palette[addr] = val; - PaletteDirty |= 1 << (addr / VRAMDirtyGranularity); -} - -template -T ReadOAM(u32 addr) -{ - return *(T*)&OAM[addr & 0x7FF]; -} - -template -void WriteOAM(u32 addr, T val) -{ - addr &= 0x7FF; - - *(T*)&OAM[addr] = val; - OAMDirty |= 1 << (addr / 1024); -} - -void SetPowerCnt(u32 val); - -void StartFrame(); -void FinishFrame(u32 lines); -void StartScanline(u32 line); -void StartHBlank(u32 line); - -void DisplayFIFO(u32 x); - -void SetDispStat(u32 cpu, u16 val); - -void SetVCount(u16 val); } #endif diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index fe01ebb9..be6a5987 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,6 +22,8 @@ #include "GPU.h" #include "GPU3D.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -85,10 +87,8 @@ using Platform::LogLevel; namespace GPU2D { - -Unit::Unit(u32 num) +Unit::Unit(u32 num, melonDS::GPU& gpu) : Num(num), GPU(gpu) { - Num = num; } void Unit::Reset() @@ -288,10 +288,10 @@ void Unit::Write8(u32 addr, u8 val) return; case 0x10: - if (!Num) GPU3D::SetRenderXPos((GPU3D::RenderXPos & 0xFF00) | val); + if (!Num) GPU.GPU3D.SetRenderXPos((GPU.GPU3D.GetRenderXPos() & 0xFF00) | val); break; case 0x11: - if (!Num) GPU3D::SetRenderXPos((GPU3D::RenderXPos & 0x00FF) | (val << 8)); + if (!Num) GPU.GPU3D.SetRenderXPos((GPU.GPU3D.GetRenderXPos() & 0x00FF) | (val << 8)); break; } @@ -384,7 +384,7 @@ void Unit::Write16(u32 addr, u16 val) return; case 0x010: - if (!Num) GPU3D::SetRenderXPos(val); + if (!Num) GPU.GPU3D.SetRenderXPos(val); break; case 0x068: @@ -423,21 +423,21 @@ void Unit::Write16(u32 addr, u16 val) case 0x026: BGRotD[0] = val; return; case 0x028: BGXRef[0] = (BGXRef[0] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0]; + if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0]; return; case 0x02A: if (val & 0x0800) val |= 0xF000; BGXRef[0] = (BGXRef[0] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0]; + if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0]; return; case 0x02C: BGYRef[0] = (BGYRef[0] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0]; + if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0]; return; case 0x02E: if (val & 0x0800) val |= 0xF000; BGYRef[0] = (BGYRef[0] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0]; + if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0]; return; case 0x030: BGRotA[1] = val; return; @@ -446,21 +446,21 @@ void Unit::Write16(u32 addr, u16 val) case 0x036: BGRotD[1] = val; return; case 0x038: BGXRef[1] = (BGXRef[1] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1]; + if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1]; return; case 0x03A: if (val & 0x0800) val |= 0xF000; BGXRef[1] = (BGXRef[1] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1]; + if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1]; return; case 0x03C: BGYRef[1] = (BGYRef[1] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1]; + if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1]; return; case 0x03E: if (val & 0x0800) val |= 0xF000; BGYRef[1] = (BGYRef[1] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1]; + if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1]; return; case 0x040: @@ -542,23 +542,23 @@ void Unit::Write32(u32 addr, u32 val) case 0x028: if (val & 0x08000000) val |= 0xF0000000; BGXRef[0] = val; - if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0]; + if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0]; return; case 0x02C: if (val & 0x08000000) val |= 0xF0000000; BGYRef[0] = val; - if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0]; + if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0]; return; case 0x038: if (val & 0x08000000) val |= 0xF0000000; BGXRef[1] = val; - if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1]; + if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1]; return; case 0x03C: if (val & 0x08000000) val |= 0xF0000000; BGYRef[1] = val; - if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1]; + if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1]; return; } } @@ -629,15 +629,15 @@ u16* Unit::GetBGExtPal(u32 slot, u32 pal) const u32 PaletteSize = 256 * 2; const u32 SlotSize = PaletteSize * 16; return (u16*)&(Num == 0 - ? GPU::VRAMFlat_ABGExtPal - : GPU::VRAMFlat_BBGExtPal)[slot * SlotSize + pal * PaletteSize]; + ? GPU.VRAMFlat_ABGExtPal + : GPU.VRAMFlat_BBGExtPal)[slot * SlotSize + pal * PaletteSize]; } u16* Unit::GetOBJExtPal() { return Num == 0 - ? (u16*)GPU::VRAMFlat_AOBJExtPal - : (u16*)GPU::VRAMFlat_BOBJExtPal; + ? (u16*)GPU.VRAMFlat_AOBJExtPal + : (u16*)GPU.VRAMFlat_BOBJExtPal; } void Unit::CheckWindows(u32 line) @@ -649,7 +649,7 @@ void Unit::CheckWindows(u32 line) else if (line == Win1Coords[2]) Win1Active |= 0x1; } -void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow) +void Unit::CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow) { for (u32 i = 0; i < 256; i++) windowMask[i] = WinCnt[2]; // window outside @@ -695,32 +695,33 @@ void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow) } } -void Unit::GetBGVRAM(u8*& data, u32& mask) +void Unit::GetBGVRAM(u8*& data, u32& mask) const { if (Num == 0) { - data = GPU::VRAMFlat_ABG; + data = GPU.VRAMFlat_ABG; mask = 0x7FFFF; } else { - data = GPU::VRAMFlat_BBG; + data = GPU.VRAMFlat_BBG; mask = 0x1FFFF; } } -void Unit::GetOBJVRAM(u8*& data, u32& mask) +void Unit::GetOBJVRAM(u8*& data, u32& mask) const { if (Num == 0) { - data = GPU::VRAMFlat_AOBJ; + data = GPU.VRAMFlat_AOBJ; mask = 0x3FFFF; } else { - data = GPU::VRAMFlat_BOBJ; + data = GPU.VRAMFlat_BOBJ; mask = 0x1FFFF; } } } +} diff --git a/src/GPU2D.h b/src/GPU2D.h index 5edc0a2c..e87167cb 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,14 +22,20 @@ #include "types.h" #include "Savestate.h" +namespace melonDS +{ +class GPU; + namespace GPU2D { class Unit { public: - Unit(u32 num); - + // take a reference to the GPU so we can access its state + // and ensure that it's not null + Unit(u32 num, melonDS::GPU& gpu); + virtual ~Unit() = default; Unit(const Unit&) = delete; Unit& operator=(const Unit&) = delete; @@ -46,7 +52,7 @@ public: void Write16(u32 addr, u16 val); void Write32(u32 addr, u32 val); - bool UsesFIFO() + bool UsesFIFO() const { if (((DispCnt >> 16) & 0x3) == 3) return true; @@ -66,11 +72,11 @@ public: u16* GetBGExtPal(u32 slot, u32 pal); u16* GetOBJExtPal(); - void GetBGVRAM(u8*& data, u32& mask); - void GetOBJVRAM(u8*& data, u32& mask); + void GetBGVRAM(u8*& data, u32& mask) const; + void GetOBJVRAM(u8*& data, u32& mask) const; void UpdateMosaicCounters(u32 line); - void CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow); + void CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow); u32 Num; bool Enabled; @@ -116,6 +122,8 @@ public: u32 CaptureCnt; u16 MasterBrightness; +private: + melonDS::GPU& GPU; }; class Renderer2D @@ -141,4 +149,5 @@ protected: } +} #endif diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 3704de91..26f9a875 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,77 +20,17 @@ #include "GPU.h" #include "GPU3D.h" +namespace melonDS +{ namespace GPU2D { - -SoftRenderer::SoftRenderer() - : Renderer2D() +SoftRenderer::SoftRenderer(melonDS::GPU& gpu) + : Renderer2D(), GPU(gpu) { - // initialize mosaic table - for (int m = 0; m < 16; m++) - { - for (int x = 0; x < 256; x++) - { - int offset = x % (m+1); - MosaicTable[m][x] = offset; - } - } + // mosaic table is initialized at compile-time } -u32 SoftRenderer::ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb) -{ - u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4; - u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00; - u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000; - - if (r > 0x00003F) r = 0x00003F; - if (g > 0x003F00) g = 0x003F00; - if (b > 0x3F0000) b = 0x3F0000; - - return r | g | b | 0xFF000000; -} - -u32 SoftRenderer::ColorBlend5(u32 val1, u32 val2) -{ - u32 eva = ((val1 >> 24) & 0x1F) + 1; - u32 evb = 32 - eva; - - if (eva == 32) return val1; - - u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5; - u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00; - u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000; - - if (r > 0x00003F) r = 0x00003F; - if (g > 0x003F00) g = 0x003F00; - if (b > 0x3F0000) b = 0x3F0000; - - return r | g | b | 0xFF000000; -} - -u32 SoftRenderer::ColorBrightnessUp(u32 val, u32 factor, u32 bias) -{ - u32 rb = val & 0x3F003F; - u32 g = val & 0x003F00; - - rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F); - g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00); - - return rb | g | 0xFF000000; -} - -u32 SoftRenderer::ColorBrightnessDown(u32 val, u32 factor, u32 bias) -{ - u32 rb = val & 0x3F003F; - u32 g = val & 0x003F00; - - rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F); - g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00); - - return rb | g | 0xFF000000; -} - -u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) +u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) const { u32 coloreffect = 0; u32 eva, evb; @@ -166,29 +106,29 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) { CurUnit = unit; - int stride = GPU3D::CurrentRenderer->Accelerated ? (256*3 + 1) : 256; + int stride = GPU.GPU3D.IsRendererAccelerated() ? (256*3 + 1) : 256; u32* dst = &Framebuffer[CurUnit->Num][stride * line]; int n3dline = line; - line = GPU::VCount; + line = GPU.VCount; if (CurUnit->Num == 0) { - auto bgDirty = GPU::VRAMDirty_ABG.DeriveState(GPU::VRAMMap_ABG); - GPU::MakeVRAMFlat_ABGCoherent(bgDirty); - auto bgExtPalDirty = GPU::VRAMDirty_ABGExtPal.DeriveState(GPU::VRAMMap_ABGExtPal); - GPU::MakeVRAMFlat_ABGExtPalCoherent(bgExtPalDirty); - auto objExtPalDirty = GPU::VRAMDirty_AOBJExtPal.DeriveState(&GPU::VRAMMap_AOBJExtPal); - GPU::MakeVRAMFlat_AOBJExtPalCoherent(objExtPalDirty); + auto bgDirty = GPU.VRAMDirty_ABG.DeriveState(GPU.VRAMMap_ABG, GPU); + GPU.MakeVRAMFlat_ABGCoherent(bgDirty); + auto bgExtPalDirty = GPU.VRAMDirty_ABGExtPal.DeriveState(GPU.VRAMMap_ABGExtPal, GPU); + GPU.MakeVRAMFlat_ABGExtPalCoherent(bgExtPalDirty); + auto objExtPalDirty = GPU.VRAMDirty_AOBJExtPal.DeriveState(&GPU.VRAMMap_AOBJExtPal, GPU); + GPU.MakeVRAMFlat_AOBJExtPalCoherent(objExtPalDirty); } else { - auto bgDirty = GPU::VRAMDirty_BBG.DeriveState(GPU::VRAMMap_BBG); - GPU::MakeVRAMFlat_BBGCoherent(bgDirty); - auto bgExtPalDirty = GPU::VRAMDirty_BBGExtPal.DeriveState(GPU::VRAMMap_BBGExtPal); - GPU::MakeVRAMFlat_BBGExtPalCoherent(bgExtPalDirty); - auto objExtPalDirty = GPU::VRAMDirty_BOBJExtPal.DeriveState(&GPU::VRAMMap_BOBJExtPal); - GPU::MakeVRAMFlat_BOBJExtPalCoherent(objExtPalDirty); + auto bgDirty = GPU.VRAMDirty_BBG.DeriveState(GPU.VRAMMap_BBG, GPU); + GPU.MakeVRAMFlat_BBGCoherent(bgDirty); + auto bgExtPalDirty = GPU.VRAMDirty_BBGExtPal.DeriveState(GPU.VRAMMap_BBGExtPal, GPU); + GPU.MakeVRAMFlat_BBGExtPalCoherent(bgExtPalDirty); + auto objExtPalDirty = GPU.VRAMDirty_BOBJExtPal.DeriveState(&GPU.VRAMMap_BOBJExtPal, GPU); + GPU.MakeVRAMFlat_BOBJExtPalCoherent(objExtPalDirty); } bool forceblank = false; @@ -206,11 +146,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) if (CurUnit->Num == 0) { - if (!GPU3D::CurrentRenderer->Accelerated) - _3DLine = GPU3D::GetLine(n3dline); + if (!GPU.GPU3D.IsRendererAccelerated()) + _3DLine = GPU.GPU3D.GetLine(n3dline); else if (CurUnit->CaptureLatch && (((CurUnit->CaptureCnt >> 29) & 0x3) != 1)) { - _3DLine = GPU3D::GetLine(n3dline); + _3DLine = GPU.GPU3D.GetLine(n3dline); //GPU3D::GLRenderer::PrepareCaptureFrame(); } } @@ -220,7 +160,7 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) for (int i = 0; i < 256; i++) dst[i] = 0xFFFFFFFF; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { dst[256*3] = 0; } @@ -254,9 +194,9 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) case 2: // VRAM display { u32 vrambank = (CurUnit->DispCnt >> 18) & 0x3; - if (GPU::VRAMMap_LCDC & (1<MasterBrightness; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { dst[256*3] = masterBrightness | (CurUnit->DispCnt & 0x30000); return; @@ -364,11 +304,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB) { #ifdef OGLRENDERER_ENABLED - if (GPU3D::CurrentRenderer->Accelerated) + if (Renderer3D& renderer3d = GPU.GPU3D.GetCurrentRenderer(); renderer3d.Accelerated) { if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1)) { - GPU3D::CurrentRenderer.get()->PrepareCaptureFrame(); + renderer3d.PrepareCaptureFrame(); } } #endif @@ -381,10 +321,10 @@ void SoftRenderer::DoCapture(u32 line, u32 width) // TODO: confirm this // it should work like VRAM display mode, which requires VRAM to be mapped to LCDC - if (!(GPU::VRAMMap_LCDC & (1<> 18) & 0x3) << 14) + (line * width); // TODO: handle 3D in GPU3D::CurrentRenderer->Accelerated mode!! @@ -397,7 +337,7 @@ void SoftRenderer::DoCapture(u32 line, u32 width) else { srcA = BGOBJLine; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { // in GPU3D::CurrentRenderer->Accelerated mode, compositing is normally done on the GPU // but when doing display capture, we do need the composited output @@ -469,8 +409,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width) else { u32 srcvram = (CurUnit->DispCnt >> 18) & 0x3; - if (GPU::VRAMMap_LCDC & (1<DispCnt >> 16) & 0x3) != 2) srcBaddr += ((captureCnt >> 26) & 0x3) << 14; @@ -479,8 +419,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width) dstaddr &= 0xFFFF; srcBaddr &= 0xFFFF; - static_assert(GPU::VRAMDirtyGranularity == 512, ""); - GPU::VRAMDirty[dstvram][(dstaddr * 2) / GPU::VRAMDirtyGranularity] = true; + static_assert(VRAMDirtyGranularity == 512); + GPU.VRAMDirty[dstvram][(dstaddr * 2) / VRAMDirtyGranularity] = true; switch ((captureCnt >> 29) & 0x3) { @@ -601,12 +541,12 @@ void SoftRenderer::DoCapture(u32 line, u32 width) { \ if ((bgCnt[num] & 0x0040) && (CurUnit->BGMosaicSize[0] > 0)) \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_##type(line, num); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_##type(line, num); \ else DrawBG_##type(line, num); \ } \ else \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_##type(line, num); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_##type(line, num); \ else DrawBG_##type(line, num); \ } \ } while (false) @@ -616,18 +556,18 @@ void SoftRenderer::DoCapture(u32 line, u32 width) { \ if ((bgCnt[2] & 0x0040) && (CurUnit->BGMosaicSize[0] > 0)) \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_Large(line); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_Large(line); \ else DrawBG_Large(line); \ } \ else \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_Large(line); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_Large(line); \ else DrawBG_Large(line); \ } \ } while (false) #define DoInterleaveSprites(prio) \ - if (GPU3D::CurrentRenderer->Accelerated) InterleaveSprites(prio); else InterleaveSprites(prio); + if (GPU.GPU3D.IsRendererAccelerated()) InterleaveSprites(prio); else InterleaveSprites(prio); template void SoftRenderer::DrawScanlineBGMode(u32 line) @@ -757,8 +697,8 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line) } u64 backdrop; - if (CurUnit->Num) backdrop = *(u16*)&GPU::Palette[0x400]; - else backdrop = *(u16*)&GPU::Palette[0]; + if (CurUnit->Num) backdrop = *(u16*)&GPU.Palette[0x400]; + else backdrop = *(u16*)&GPU.Palette[0]; { u8 r = (backdrop & 0x001F) << 1; @@ -778,7 +718,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line) memset(WindowMask, 0xFF, 256); ApplySpriteMosaicX(); - CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]]; + CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]].data(); switch (CurUnit->DispCnt & 0x7) { @@ -795,7 +735,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line) // color special effects // can likely be optimized - if (!GPU3D::CurrentRenderer->Accelerated) + if (!GPU.GPU3D.IsRendererAccelerated()) { for (int i = 0; i < 256; i++) { @@ -941,7 +881,7 @@ void SoftRenderer::DrawBG_3D() { int i = 0; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { for (i = 0; i < 256; i++) { @@ -998,14 +938,14 @@ void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum) tilesetaddr = ((bgcnt & 0x003C) << 12); tilemapaddr = ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0x400]; + pal = (u16*)&GPU.Palette[0x400]; } else { tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12); tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0]; + pal = (u16*)&GPU.Palette[0]; } // adjust Y position in tilemap @@ -1177,14 +1117,14 @@ void SoftRenderer::DrawBG_Affine(u32 line, u32 bgnum) tilesetaddr = ((bgcnt & 0x003C) << 12); tilemapaddr = ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0x400]; + pal = (u16*)&GPU.Palette[0x400]; } else { tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12); tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0]; + pal = (u16*)&GPU.Palette[0]; } u16 curtile; @@ -1331,8 +1271,8 @@ void SoftRenderer::DrawBG_Extended(u32 line, u32 bgnum) { // 256-color bitmap - if (CurUnit->Num) pal = (u16*)&GPU::Palette[0x400]; - else pal = (u16*)&GPU::Palette[0]; + if (CurUnit->Num) pal = (u16*)&GPU.Palette[0x400]; + else pal = (u16*)&GPU.Palette[0]; u8 color; @@ -1390,14 +1330,14 @@ void SoftRenderer::DrawBG_Extended(u32 line, u32 bgnum) tilesetaddr = ((bgcnt & 0x003C) << 12); tilemapaddr = ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0x400]; + pal = (u16*)&GPU.Palette[0x400]; } else { tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12); tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0]; + pal = (u16*)&GPU.Palette[0]; } u16 curtile; @@ -1508,8 +1448,8 @@ void SoftRenderer::DrawBG_Large(u32 line) // BG is always BG2 // 256-color bitmap - if (CurUnit->Num) pal = (u16*)&GPU::Palette[0x400]; - else pal = (u16*)&GPU::Palette[0]; + if (CurUnit->Num) pal = (u16*)&GPU.Palette[0x400]; + else pal = (u16*)&GPU.Palette[0]; u8 color; @@ -1563,7 +1503,7 @@ void SoftRenderer::ApplySpriteMosaicX() u32* objLine = OBJLine[CurUnit->Num]; - u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]]; + u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]].data(); u32 lastcolor = objLine[0]; @@ -1582,7 +1522,7 @@ template void SoftRenderer::InterleaveSprites(u32 prio) { u32* objLine = OBJLine[CurUnit->Num]; - u16* pal = (u16*)&GPU::Palette[CurUnit->Num ? 0x600 : 0x200]; + u16* pal = (u16*)&GPU.Palette[CurUnit->Num ? 0x600 : 0x200]; if (CurUnit->DispCnt & 0x80000000) { @@ -1656,13 +1596,13 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit) if (CurUnit->Num == 0) { - auto objDirty = GPU::VRAMDirty_AOBJ.DeriveState(GPU::VRAMMap_AOBJ); - GPU::MakeVRAMFlat_AOBJCoherent(objDirty); + auto objDirty = GPU.VRAMDirty_AOBJ.DeriveState(GPU.VRAMMap_AOBJ, GPU); + GPU.MakeVRAMFlat_AOBJCoherent(objDirty); } else { - auto objDirty = GPU::VRAMDirty_BOBJ.DeriveState(GPU::VRAMMap_BOBJ); - GPU::MakeVRAMFlat_BOBJCoherent(objDirty); + auto objDirty = GPU.VRAMDirty_BOBJ.DeriveState(GPU.VRAMMap_BOBJ, GPU); + GPU.MakeVRAMFlat_BOBJCoherent(objDirty); } NumSprites[CurUnit->Num] = 0; @@ -1670,7 +1610,7 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit) memset(OBJWindow[CurUnit->Num], 0, 256); if (!(CurUnit->DispCnt & 0x1000)) return; - u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0]; + u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0]; const s32 spritewidth[16] = { @@ -1765,7 +1705,7 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit) template void SoftRenderer::DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos) { - u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0]; + u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0]; u16* attrib = &oam[num * 4]; u16* rotparams = &oam[(((attrib[1] >> 9) & 0x1F) * 16) + 3]; @@ -1977,7 +1917,7 @@ void SoftRenderer::DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight, template void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s32 ypos) { - u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0]; + u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0]; u16* attrib = &oam[num * 4]; u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000; @@ -2226,3 +2166,4 @@ void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s } } +} diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index a9fff972..befb67f6 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,19 +20,24 @@ #include "GPU2D.h" +namespace melonDS +{ +class GPU; + namespace GPU2D { class SoftRenderer : public Renderer2D { public: - SoftRenderer(); + SoftRenderer(melonDS::GPU& gpu); ~SoftRenderer() override {} void DrawScanline(u32 line, Unit* unit) override; void DrawSprites(u32 line, Unit* unit) override; void VBlankEnd(Unit* unitA, Unit* unitB) override; private: + melonDS::GPU& GPU; alignas(8) u32 BGOBJLine[256*3]; u32* _3DLine; @@ -44,13 +49,75 @@ private: u32 NumSprites[2]; u8* CurBGXMosaicTable; - u8 MosaicTable[16][256]; + array2d MosaicTable = []() constexpr + { + array2d table {}; + // initialize mosaic table + for (int m = 0; m < 16; m++) + { + for (int x = 0; x < 256; x++) + { + int offset = x % (m+1); + table[m][x] = offset; + } + } - u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb); - u32 ColorBlend5(u32 val1, u32 val2); - u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias); - u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias); - u32 ColorComposite(int i, u32 val1, u32 val2); + return table; + }(); + + static constexpr u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb) noexcept + { + u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4; + u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00; + u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000; + + if (r > 0x00003F) r = 0x00003F; + if (g > 0x003F00) g = 0x003F00; + if (b > 0x3F0000) b = 0x3F0000; + + return r | g | b | 0xFF000000; + } + + static constexpr u32 ColorBlend5(u32 val1, u32 val2) noexcept + { + u32 eva = ((val1 >> 24) & 0x1F) + 1; + u32 evb = 32 - eva; + + if (eva == 32) return val1; + + u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5; + u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00; + u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000; + + if (r > 0x00003F) r = 0x00003F; + if (g > 0x003F00) g = 0x003F00; + if (b > 0x3F0000) b = 0x3F0000; + + return r | g | b | 0xFF000000; + } + + static constexpr u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias) noexcept + { + u32 rb = val & 0x3F003F; + u32 g = val & 0x003F00; + + rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F); + g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00); + + return rb | g | 0xFF000000; + } + + static constexpr u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias) noexcept + { + u32 rb = val & 0x3F003F; + u32 g = val & 0x003F00; + + rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F); + g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00); + + return rb | g | 0xFF000000; + } + u32 ColorComposite(int i, u32 val1, u32 val2) const; template void DrawScanlineBGMode(u32 line); void DrawScanlineBGMode6(u32 line); @@ -78,3 +145,5 @@ private: }; } + +} \ No newline at end of file diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 10b3dced..44561dfa 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,9 +22,12 @@ #include "NDS.h" #include "GPU.h" #include "FIFO.h" +#include "GPU3D_Soft.h" #include "Platform.h" #include "GPU3D.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -99,9 +102,6 @@ using Platform::LogLevel; // * additionally, some commands (BEGIN, LIGHT_VECTOR, BOXTEST) stall the polygon pipeline -namespace GPU3D -{ - const u8 CmdNumParams[256] = { // 0x00 @@ -139,158 +139,34 @@ const u8 CmdNumParams[256] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -typedef union -{ - u64 _contents; - struct - { - u32 Param; - u8 Command; - }; - -} CmdFIFOEntry; - -FIFO CmdFIFO; -FIFO CmdPIPE; - -FIFO CmdStallQueue; - -u32 NumCommands, CurCommand, ParamCount, TotalParams; - -bool GeometryEnabled; -bool RenderingEnabled; - -u32 DispCnt; -u8 AlphaRefVal, AlphaRef; - -u16 ToonTable[32]; -u16 EdgeTable[8]; - -u32 FogColor, FogOffset; -u8 FogDensityTable[32]; - -u32 ClearAttr1, ClearAttr2; - -u32 RenderDispCnt; -u8 RenderAlphaRef; - -u16 RenderToonTable[32]; -u16 RenderEdgeTable[8]; - -u32 RenderFogColor, RenderFogOffset, RenderFogShift; -u8 RenderFogDensityTable[34]; - -u32 RenderClearAttr1, RenderClearAttr2; - -bool RenderFrameIdentical; - -u16 RenderXPos; - -u32 ZeroDotWLimit; - -u32 GXStat; - -u32 ExecParams[32]; -u32 ExecParamCount; - -u64 Timestamp; -s32 CycleCount; -s32 VertexPipeline; -s32 NormalPipeline; -s32 PolygonPipeline; -s32 VertexSlotCounter; -u32 VertexSlotsFree; - -u32 NumPushPopCommands; -u32 NumTestCommands; - - -u32 MatrixMode; - -s32 ProjMatrix[16]; -s32 PosMatrix[16]; -s32 VecMatrix[16]; -s32 TexMatrix[16]; - -s32 ClipMatrix[16]; -bool ClipMatrixDirty; - -u32 Viewport[6]; - -s32 ProjMatrixStack[16]; -s32 PosMatrixStack[32][16]; -s32 VecMatrixStack[32][16]; -s32 TexMatrixStack[16]; -s32 ProjMatrixStackPointer; -s32 PosMatrixStackPointer; -s32 TexMatrixStackPointer; - void MatrixLoadIdentity(s32* m); -void UpdateClipMatrix(); - -u32 PolygonMode; -s16 CurVertex[3]; -u8 VertexColor[3]; -s16 TexCoords[2]; -s16 RawTexCoords[2]; -s16 Normal[3]; - -s16 LightDirection[4][3]; -u8 LightColor[4][3]; -u8 MatDiffuse[3]; -u8 MatAmbient[3]; -u8 MatSpecular[3]; -u8 MatEmission[3]; - -bool UseShininessTable; -u8 ShininessTable[128]; - -u32 PolygonAttr; -u32 CurPolygonAttr; - -u32 TexParam; -u32 TexPalette; - -s32 PosTestResult[4]; -s16 VecTestResult[3]; - -Vertex TempVertexBuffer[4]; -u32 VertexNum; -u32 VertexNumInPoly; -u32 NumConsecutivePolygons; -Polygon* LastStripPolygon; -u32 NumOpaquePolygons; - -Vertex VertexRAM[6144 * 2]; -Polygon PolygonRAM[2048 * 2]; - -Vertex* CurVertexRAM; -Polygon* CurPolygonRAM; -u32 NumVertices, NumPolygons; -u32 CurRAMBank; - -std::array RenderPolygonRAM; -u32 RenderNumPolygons; - -u32 FlushRequest; -u32 FlushAttributes; - -std::unique_ptr CurrentRenderer = {}; - -bool AbortFrame; - -bool Init() +GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer) noexcept : + NDS(nds), + CurrentRenderer(renderer ? std::move(renderer) : std::make_unique()) { - return true; } -void DeInit() +void Vertex::DoSavestate(Savestate* file) noexcept { - CurrentRenderer = nullptr; + file->VarArray(Position, sizeof(Position)); + file->VarArray(Color, sizeof(Color)); + file->VarArray(TexCoords, sizeof(TexCoords)); + + file->Bool32(&Clipped); + + file->VarArray(FinalPosition, sizeof(FinalPosition)); + file->VarArray(FinalColor, sizeof(FinalColor)); + file->VarArray(HiresPosition, sizeof(HiresPosition)); } -void ResetRenderingState() +void GPU3D::SetCurrentRenderer(std::unique_ptr&& renderer) noexcept +{ + CurrentRenderer = std::move(renderer); + CurrentRenderer->Reset(NDS.GPU); +} + +void GPU3D::ResetRenderingState() noexcept { RenderNumPolygons = 0; @@ -309,24 +185,13 @@ void ResetRenderingState() RenderClearAttr2 = 0x00007FFF; } -void Reset() +void GPU3D::Reset() noexcept { CmdFIFO.Clear(); CmdPIPE.Clear(); CmdStallQueue.Clear(); - NumCommands = 0; - CurCommand = 0; - ParamCount = 0; - TotalParams = 0; - - NumPushPopCommands = 0; - NumTestCommands = 0; - - DispCnt = 0; - AlphaRef = 0; - ZeroDotWLimit = 0; // CHECKME GXStat = 0; @@ -334,7 +199,6 @@ void Reset() memset(ExecParams, 0, 32*4); ExecParamCount = 0; - Timestamp = 0; CycleCount = 0; VertexPipeline = 0; NormalPipeline = 0; @@ -342,6 +206,8 @@ void Reset() VertexSlotCounter = 0; VertexSlotsFree = 1; + NumPushPopCommands = 0; + NumTestCommands = 0; MatrixMode = 0; @@ -359,41 +225,98 @@ void Reset() memset(PosMatrixStack, 0, 31 * 16*4); memset(VecMatrixStack, 0, 31 * 16*4); memset(TexMatrixStack, 0, 16*4); + ProjMatrixStackPointer = 0; PosMatrixStackPointer = 0; TexMatrixStackPointer = 0; + NumCommands = 0; + CurCommand = 0; + ParamCount = 0; + TotalParams = 0; + + GeometryEnabled = false; + RenderingEnabled = false; + + DispCnt = 0; + AlphaRefVal = 0; + AlphaRef = 0; + + memset(ToonTable, 0, sizeof(ToonTable)); + memset(EdgeTable, 0, sizeof(EdgeTable)); + + // TODO: confirm initial polyid/color/fog values + FogOffset = 0; + FogColor = 0; + memset(FogDensityTable, 0, sizeof(FogDensityTable)); + + ClearAttr1 = 0x3F000000; + ClearAttr2 = 0x00007FFF; + + ResetRenderingState(); + + AbortFrame = false; + + Timestamp = 0; + + PolygonMode = 0; + memset(CurVertex, 0, sizeof(CurVertex)); + memset(VertexColor, 0, sizeof(VertexColor)); + memset(TexCoords, 0, sizeof(TexCoords)); + memset(RawTexCoords, 0, sizeof(RawTexCoords)); + memset(Normal, 0, sizeof(Normal)); + + memset(LightDirection, 0, sizeof(LightDirection)); + memset(LightColor, 0, sizeof(LightColor)); + memset(MatDiffuse, 0, sizeof(MatDiffuse)); + memset(MatAmbient, 0, sizeof(MatAmbient)); + memset(MatSpecular, 0, sizeof(MatSpecular)); + memset(MatEmission, 0, sizeof(MatSpecular)); + + UseShininessTable = false; + memset(ShininessTable, 0, sizeof(ShininessTable)); + + PolygonAttr = 0; + CurPolygonAttr = 0; + + TexParam = 0; + TexPalette = 0; + memset(PosTestResult, 0, 4*4); memset(VecTestResult, 0, 2*3); + memset(TempVertexBuffer, 0, sizeof(TempVertexBuffer)); VertexNum = 0; VertexNumInPoly = 0; + NumConsecutivePolygons = 0; + LastStripPolygon = nullptr; + NumOpaquePolygons = 0; - CurRAMBank = 0; CurVertexRAM = &VertexRAM[0]; CurPolygonRAM = &PolygonRAM[0]; NumVertices = 0; NumPolygons = 0; - NumOpaquePolygons = 0; - - // TODO: confirm initial polyid/color/fog values - ClearAttr1 = 0x3F000000; - ClearAttr2 = 0x00007FFF; + CurRAMBank = 0; FlushRequest = 0; FlushAttributes = 0; - ResetRenderingState(); - RenderXPos = 0; - AbortFrame = false; + if (CurrentRenderer) + CurrentRenderer->Reset(NDS.GPU); } -void DoSavestate(Savestate* file) +void GPU3D::DoSavestate(Savestate* file) noexcept { file->Section("GP3D"); + SoftRenderer* softRenderer = dynamic_cast(CurrentRenderer.get()); + if (softRenderer && softRenderer->IsThreaded()) + { + softRenderer->SetupRenderThread(NDS.GPU); + } + CmdFIFO.DoSavestate(file); CmdPIPE.DoSavestate(file); @@ -469,33 +392,21 @@ void DoSavestate(Savestate* file) file->Var32(&VertexNumInPoly); file->Var32(&NumConsecutivePolygons); - for (int i = 0; i < 4; i++) + for (Vertex& vtx : TempVertexBuffer) { - Vertex* vtx = &TempVertexBuffer[i]; - - file->VarArray(vtx->Position, sizeof(s32)*4); - file->VarArray(vtx->Color, sizeof(s32)*3); - file->VarArray(vtx->TexCoords, sizeof(s16)*2); - - file->Bool32(&vtx->Clipped); - - file->VarArray(vtx->FinalPosition, sizeof(s32)*2); - file->VarArray(vtx->FinalColor, sizeof(s32)*3); + vtx.DoSavestate(file); } if (file->Saving) { - u32 id; - if (LastStripPolygon) id = (u32)((LastStripPolygon - (&PolygonRAM[0])) / sizeof(Polygon)); - else id = -1; - file->Var32(&id); + u32 index = LastStripPolygon ? (u32)(LastStripPolygon - &PolygonRAM[0]) : UINT32_MAX; + file->Var32(&index); } else { - u32 id; - file->Var32(&id); - if (id == 0xFFFFFFFF) LastStripPolygon = NULL; - else LastStripPolygon = &PolygonRAM[id]; + u32 index = UINT32_MAX; + file->Var32(&index); + LastStripPolygon = (index == UINT32_MAX) ? nullptr : &PolygonRAM[index]; } file->Var32(&CurRAMBank); @@ -506,18 +417,9 @@ void DoSavestate(Savestate* file) file->Var32(&FlushRequest); file->Var32(&FlushAttributes); - for (int i = 0; i < 6144*2; i++) + for (Vertex& vtx : VertexRAM) { - Vertex* vtx = &VertexRAM[i]; - - file->VarArray(vtx->Position, sizeof(s32)*4); - file->VarArray(vtx->Color, sizeof(s32)*3); - file->VarArray(vtx->TexCoords, sizeof(s16)*2); - - file->Bool32(&vtx->Clipped); - - file->VarArray(vtx->FinalPosition, sizeof(s32)*2); - file->VarArray(vtx->FinalColor, sizeof(s32)*3); + vtx.DoSavestate(file); } for(int i = 0; i < 2048*2; i++) @@ -531,20 +433,17 @@ void DoSavestate(Savestate* file) for (int j = 0; j < 10; j++) { Vertex* ptr = poly->Vertices[j]; - u32 id; - if (ptr) id = (u32)((ptr - (&VertexRAM[0])) / sizeof(Vertex)); - else id = -1; - file->Var32(&id); + u32 index = ptr ? (u32)(ptr - &VertexRAM[0]) : UINT32_MAX; + file->Var32(&index); } } else { for (int j = 0; j < 10; j++) { - u32 id = -1; - file->Var32(&id); - if (id == 0xFFFFFFFF) poly->Vertices[j] = NULL; - else poly->Vertices[j] = &VertexRAM[id]; + u32 index = UINT32_MAX; + file->Var32(&index); + poly->Vertices[j] = index == UINT32_MAX ? nullptr : &VertexRAM[index]; } } @@ -592,7 +491,6 @@ void DoSavestate(Savestate* file) } } - // probably not worth storing the vblank-latched Renderxxxxxx variables CmdStallQueue.DoSavestate(file); file->Var32((u32*)&VertexPipeline); @@ -608,10 +506,27 @@ void DoSavestate(Savestate* file) CurVertexRAM = &VertexRAM[CurRAMBank ? 6144 : 0]; CurPolygonRAM = &PolygonRAM[CurRAMBank ? 2048 : 0]; + } - // better safe than sorry, I guess - // might cause a blank frame but atleast it won't shit itself - RenderNumPolygons = 0; + file->Var32(&RenderNumPolygons); + if (file->Saving) + { + for (const Polygon* p : RenderPolygonRAM) + { + u32 index = p ? (p - &PolygonRAM[0]) : UINT32_MAX; + + file->Var32(&index); + } + } + else + { + for (int i = 0; i < RenderPolygonRAM.size(); ++i) + { + u32 index = UINT32_MAX; + file->Var32(&index); + + RenderPolygonRAM[i] = index == UINT32_MAX ? nullptr : &PolygonRAM[index]; + } } file->VarArray(CurVertex, sizeof(s16)*3); @@ -631,11 +546,23 @@ void DoSavestate(Savestate* file) file->VarArray(ShininessTable, 128*sizeof(u8)); file->Bool32(&AbortFrame); + file->Bool32(&GeometryEnabled); + file->Bool32(&RenderingEnabled); + file->Var32(&PolygonMode); + file->Var32(&PolygonAttr); + file->Var32(&CurPolygonAttr); + file->Var32(&TexParam); + file->Var32(&TexPalette); + RenderFrameIdentical = false; + if (softRenderer && softRenderer->IsThreaded()) + { + softRenderer->EnableRenderThread(); + } } -void SetEnabled(bool geometry, bool rendering) +void GPU3D::SetEnabled(bool geometry, bool rendering) noexcept { GeometryEnabled = geometry; RenderingEnabled = rendering; @@ -768,7 +695,7 @@ void MatrixTranslate(s32* m, s32* s) m[15] += ((s64)s[0]*m[3] + (s64)s[1]*m[7] + (s64)s[2]*m[11]) >> 12; } -void UpdateClipMatrix() +void GPU3D::UpdateClipMatrix() noexcept { if (!ClipMatrixDirty) return; ClipMatrixDirty = false; @@ -779,7 +706,7 @@ void UpdateClipMatrix() -void AddCycles(s32 num) +void GPU3D::AddCycles(s32 num) noexcept { CycleCount += num; @@ -810,7 +737,7 @@ void AddCycles(s32 num) } } -void NextVertexSlot() +void GPU3D::NextVertexSlot() noexcept { s32 num = (9 - VertexSlotCounter) + 1; @@ -853,7 +780,7 @@ void NextVertexSlot() } } -void StallPolygonPipeline(s32 delay, s32 nonstalldelay) +void GPU3D::StallPolygonPipeline(s32 delay, s32 nonstalldelay) noexcept { if (PolygonPipeline > 0) { @@ -908,7 +835,7 @@ void ClipSegment(Vertex* outbuf, Vertex* vin, Vertex* vout) } template -int ClipAgainstPlane(Vertex* vertices, int nverts, int clipstart) +int ClipAgainstPlane(const GPU3D& gpu, Vertex* vertices, int nverts, int clipstart) { Vertex temp[10]; int prev, next; @@ -928,7 +855,7 @@ int ClipAgainstPlane(Vertex* vertices, int nverts, int clipstart) Vertex vtx = vertices[i]; if (vtx.Position[comp] > vtx.Position[3]) { - if ((comp == 2) && (!(CurPolygonAttr & (1<<12)))) return 0; + if ((comp == 2) && (!(gpu.CurPolygonAttr & (1<<12)))) return 0; Vertex* vprev = &vertices[prev]; if (vprev->Position[comp] <= vprev->Position[3]) @@ -989,7 +916,7 @@ int ClipAgainstPlane(Vertex* vertices, int nverts, int clipstart) } template -int ClipPolygon(Vertex* vertices, int nverts, int clipstart) +int ClipPolygon(GPU3D& gpu, Vertex* vertices, int nverts, int clipstart) { // clip. // for each vertex: @@ -1002,13 +929,13 @@ int ClipPolygon(Vertex* vertices, int nverts, int clipstart) // clipping seems to process the Y plane before the X plane. // Z clipping - nverts = ClipAgainstPlane<2, attribs>(vertices, nverts, clipstart); + nverts = ClipAgainstPlane<2, attribs>(gpu, vertices, nverts, clipstart); // Y clipping - nverts = ClipAgainstPlane<1, attribs>(vertices, nverts, clipstart); + nverts = ClipAgainstPlane<1, attribs>(gpu, vertices, nverts, clipstart); // X clipping - nverts = ClipAgainstPlane<0, attribs>(vertices, nverts, clipstart); + nverts = ClipAgainstPlane<0, attribs>(gpu, vertices, nverts, clipstart); return nverts; } @@ -1021,7 +948,7 @@ bool ClipCoordsEqual(Vertex* a, Vertex* b) a->Position[3] == b->Position[3]; } -void SubmitPolygon() +void GPU3D::SubmitPolygon() noexcept { Vertex clippedvertices[10]; Vertex* reusedvertices[2]; @@ -1071,7 +998,7 @@ void SubmitPolygon() bool facingview = (dot <= 0); - if (facingview) + if (dot < 0) { if (!(CurPolygonAttr & (1<<7))) { @@ -1154,7 +1081,7 @@ void SubmitPolygon() // clipping - nverts = ClipPolygon(clippedvertices, nverts, clipstart); + nverts = ClipPolygon(*this, clippedvertices, nverts, clipstart); if (nverts == 0) { LastStripPolygon = NULL; @@ -1426,7 +1353,7 @@ void SubmitPolygon() LastStripPolygon = NULL; } -void SubmitVertex() +void GPU3D::SubmitVertex() noexcept { s64 vertex[4] = {(s64)CurVertex[0], (s64)CurVertex[1], (s64)CurVertex[2], 0x1000}; Vertex* vertextrans = &TempVertexBuffer[VertexNumInPoly]; @@ -1524,7 +1451,7 @@ void SubmitVertex() AddCycles(3); } -void CalculateLighting() +void GPU3D::CalculateLighting() noexcept { if ((TexParam >> 30) == 2) { @@ -1599,7 +1526,7 @@ void CalculateLighting() } -void BoxTest(u32* params) +void GPU3D::BoxTest(const u32* params) noexcept { Vertex cube[8]; Vertex face[10]; @@ -1643,7 +1570,7 @@ void BoxTest(u32* params) // front face (-Z) face[0] = cube[0]; face[1] = cube[1]; face[2] = cube[2]; face[3] = cube[3]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1652,7 +1579,7 @@ void BoxTest(u32* params) // back face (+Z) face[0] = cube[4]; face[1] = cube[5]; face[2] = cube[6]; face[3] = cube[7]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1661,7 +1588,7 @@ void BoxTest(u32* params) // left face (-X) face[0] = cube[0]; face[1] = cube[3]; face[2] = cube[4]; face[3] = cube[5]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1670,7 +1597,7 @@ void BoxTest(u32* params) // right face (+X) face[0] = cube[1]; face[1] = cube[2]; face[2] = cube[7]; face[3] = cube[6]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1679,7 +1606,7 @@ void BoxTest(u32* params) // bottom face (-Y) face[0] = cube[0]; face[1] = cube[1]; face[2] = cube[6]; face[3] = cube[5]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1688,7 +1615,7 @@ void BoxTest(u32* params) // top face (+Y) face[0] = cube[2]; face[1] = cube[3]; face[2] = cube[4]; face[3] = cube[7]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1696,7 +1623,7 @@ void BoxTest(u32* params) } } -void PosTest() +void GPU3D::PosTest() noexcept { s64 vertex[4] = {(s64)CurVertex[0], (s64)CurVertex[1], (s64)CurVertex[2], 0x1000}; @@ -1709,7 +1636,7 @@ void PosTest() AddCycles(5); } -void VecTest(u32 param) +void GPU3D::VecTest(u32 param) noexcept { // TODO: maybe it overwrites the normal registers, too @@ -1732,7 +1659,7 @@ void VecTest(u32 param) -void CmdFIFOWrite(CmdFIFOEntry& entry) +void GPU3D::CmdFIFOWrite(const CmdFIFOEntry& entry) noexcept { if (CmdFIFO.IsEmpty() && !CmdPIPE.IsFull()) { @@ -1747,7 +1674,7 @@ void CmdFIFOWrite(CmdFIFOEntry& entry) // has 64 entries. this is less complicated than trying to make STMxx stall-able. CmdStallQueue.Write(entry); - NDS::GXFIFOStall(); + NDS.GXFIFOStall(); return; } @@ -1768,7 +1695,7 @@ void CmdFIFOWrite(CmdFIFOEntry& entry) } } -CmdFIFOEntry CmdFIFORead() +GPU3D::CmdFIFOEntry GPU3D::CmdFIFORead() noexcept { CmdFIFOEntry ret = CmdPIPE.Read(); @@ -1791,7 +1718,7 @@ CmdFIFOEntry CmdFIFORead() } if (CmdStallQueue.IsEmpty()) - NDS::GXFIFOUnstall(); + NDS.GXFIFOUnstall(); } CheckFIFODMA(); @@ -1801,39 +1728,7 @@ CmdFIFOEntry CmdFIFORead() return ret; } -inline void VertexPipelineSubmitCmd() -{ - // vertex commands 0x24, 0x25, 0x26, 0x27, 0x28 - if (!(VertexSlotsFree & 0x1)) NextVertexSlot(); - else AddCycles(1); - NormalPipeline = 0; -} - -inline void VertexPipelineCmdDelayed6() -{ - // commands 0x20, 0x30, 0x31, 0x72 that can run 6 cycles after a vertex - if (VertexPipeline > 2) AddCycles((VertexPipeline - 2) + 1); - else AddCycles(NormalPipeline + 1); - NormalPipeline = 0; -} - -inline void VertexPipelineCmdDelayed8() -{ - // commands 0x29, 0x2A, 0x2B, 0x33, 0x34, 0x41, 0x60, 0x71 that can run 8 cycles after a vertex - if (VertexPipeline > 0) AddCycles(VertexPipeline + 1); - else AddCycles(NormalPipeline + 1); - NormalPipeline = 0; -} - -inline void VertexPipelineCmdDelayed4() -{ - // all other commands can run 4 cycles after a vertex - // no need to do much here since that is the minimum - AddCycles(NormalPipeline + 1); - NormalPipeline = 0; -} - -void ExecuteCommand() +void GPU3D::ExecuteCommand() noexcept { CmdFIFOEntry entry = CmdFIFORead(); @@ -2431,13 +2326,13 @@ void ExecuteCommand() } } -s32 CyclesToRunFor() +s32 GPU3D::CyclesToRunFor() const noexcept { if (CycleCount < 0) return 0; return CycleCount; } -void FinishWork(s32 cycles) +void GPU3D::FinishWork(s32 cycles) noexcept { AddCycles(cycles); if (NormalPipeline) @@ -2451,18 +2346,18 @@ void FinishWork(s32 cycles) GXStat &= ~(1<<27); } -void Run() +void GPU3D::Run() noexcept { if (!GeometryEnabled || FlushRequest || (CmdPIPE.IsEmpty() && !(GXStat & (1<<27)))) { - Timestamp = NDS::ARM9Timestamp >> NDS::ARM9ClockShift; + Timestamp = NDS.ARM9Timestamp >> NDS.ARM9ClockShift; return; } - s32 cycles = (NDS::ARM9Timestamp >> NDS::ARM9ClockShift) - Timestamp; + s32 cycles = (NDS.ARM9Timestamp >> NDS.ARM9ClockShift) - Timestamp; CycleCount -= cycles; - Timestamp = NDS::ARM9Timestamp >> NDS::ARM9ClockShift; + Timestamp = NDS.ARM9Timestamp >> NDS.ARM9ClockShift; if (CycleCount <= 0) { @@ -2486,7 +2381,7 @@ void Run() } -void CheckFIFOIRQ() +void GPU3D::CheckFIFOIRQ() noexcept { bool irq = false; switch (GXStat >> 30) @@ -2495,24 +2390,30 @@ void CheckFIFOIRQ() case 2: irq = CmdFIFO.IsEmpty(); break; } - if (irq) NDS::SetIRQ(0, NDS::IRQ_GXFIFO); - else NDS::ClearIRQ(0, NDS::IRQ_GXFIFO); + if (irq) NDS.SetIRQ(0, IRQ_GXFIFO); + else NDS.ClearIRQ(0, IRQ_GXFIFO); } -void CheckFIFODMA() +void GPU3D::CheckFIFODMA() noexcept { if (CmdFIFO.Level() < 128) - NDS::CheckDMAs(0, 0x07); + NDS.CheckDMAs(0, 0x07); } -void VCount144() +void GPU3D::VCount144(GPU& gpu) noexcept { - CurrentRenderer->VCount144(); + CurrentRenderer->VCount144(gpu); } -void RestartFrame() +void GPU3D::RestartFrame(GPU& gpu) noexcept { - CurrentRenderer->RestartFrame(); + CurrentRenderer->RestartFrame(gpu); +} + +void GPU3D::Stop(const GPU& gpu) noexcept +{ + if (CurrentRenderer) + CurrentRenderer->Stop(gpu); } @@ -2528,7 +2429,7 @@ bool YSort(Polygon* a, Polygon* b) return a->SortKey < b->SortKey; } -void VBlank() +void GPU3D::VBlank() noexcept { if (GeometryEnabled) { @@ -2605,21 +2506,20 @@ void VBlank() } } -void VCount215() +void GPU3D::VCount215(GPU& gpu) noexcept { - CurrentRenderer->RenderFrame(); + CurrentRenderer->RenderFrame(gpu); } -void SetRenderXPos(u16 xpos) +void GPU3D::SetRenderXPos(u16 xpos) noexcept { if (!RenderingEnabled) return; RenderXPos = xpos & 0x01FF; } -u32 ScrolledLine[256]; -u32* GetLine(int line) +u32* GPU3D::GetLine(int line) noexcept { if (!AbortFrame) { @@ -2654,8 +2554,12 @@ u32* GetLine(int line) return ScrolledLine; } +bool GPU3D::IsRendererAccelerated() const noexcept +{ + return CurrentRenderer && CurrentRenderer->Accelerated; +} -void WriteToGXFIFO(u32 val) +void GPU3D::WriteToGXFIFO(u32 val) noexcept { if (NumCommands == 0) { @@ -2694,7 +2598,7 @@ void WriteToGXFIFO(u32 val) } -u8 Read8(u32 addr) +u8 GPU3D::Read8(u32 addr) noexcept { switch (addr) { @@ -2733,7 +2637,7 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 GPU3D::Read16(u32 addr) noexcept { switch (addr) { @@ -2777,7 +2681,7 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 GPU3D::Read32(u32 addr) noexcept { switch (addr) { @@ -2830,7 +2734,7 @@ u32 Read32(u32 addr) return 0; } -void Write8(u32 addr, u8 val) +void GPU3D::Write8(u32 addr, u8 val) noexcept { if (!RenderingEnabled && addr >= 0x04000320 && addr < 0x04000400) return; if (!GeometryEnabled && addr >= 0x04000400 && addr < 0x04000700) return; @@ -2880,7 +2784,7 @@ void Write8(u32 addr, u8 val) Log(LogLevel::Debug, "unknown GPU3D write8 %08X %02X\n", addr, val); } -void Write16(u32 addr, u16 val) +void GPU3D::Write16(u32 addr, u16 val) noexcept { if (!RenderingEnabled && addr >= 0x04000320 && addr < 0x04000400) return; if (!GeometryEnabled && addr >= 0x04000400 && addr < 0x04000700) return; @@ -2967,7 +2871,7 @@ void Write16(u32 addr, u16 val) Log(LogLevel::Debug, "unknown GPU3D write16 %08X %04X\n", addr, val); } -void Write32(u32 addr, u32 val) +void GPU3D::Write32(u32 addr, u32 val) noexcept { if (!RenderingEnabled && addr >= 0x04000320 && addr < 0x04000400) return; if (!GeometryEnabled && addr >= 0x04000400 && addr < 0x04000700) return; @@ -3064,6 +2968,12 @@ void Write32(u32 addr, u32 val) Log(LogLevel::Debug, "unknown GPU3D write32 %08X %08X\n", addr, val); } +void GPU3D::Blit(const GPU& gpu) noexcept +{ + if (CurrentRenderer) + CurrentRenderer->Blit(gpu); +} + Renderer3D::Renderer3D(bool Accelerated) : Accelerated(Accelerated) { } diff --git a/src/GPU3D.h b/src/GPU3D.h index 59c958dd..802e58b7 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,11 +22,12 @@ #include #include -#include "GPU.h" #include "Savestate.h" +#include "FIFO.h" -namespace GPU3D +namespace melonDS { +class GPU; struct Vertex { @@ -46,6 +47,7 @@ struct Vertex // TODO maybe: hi-res color? (that survives clipping) s32 HiresPosition[2]; + void DoSavestate(Savestate* file) noexcept; }; struct Polygon @@ -77,62 +79,254 @@ struct Polygon u32 SortKey; + void DoSavestate(Savestate* file) noexcept; }; -extern u32 RenderDispCnt; -extern u8 RenderAlphaRef; +class Renderer3D; +class NDS; -extern u16 RenderToonTable[32]; -extern u16 RenderEdgeTable[8]; +class GPU3D +{ +public: + GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer = nullptr) noexcept; + ~GPU3D() noexcept = default; + void Reset() noexcept; -extern u32 RenderFogColor, RenderFogOffset, RenderFogShift; -extern u8 RenderFogDensityTable[34]; + void DoSavestate(Savestate* file) noexcept; -extern u32 RenderClearAttr1, RenderClearAttr2; + void SetEnabled(bool geometry, bool rendering) noexcept; -extern bool RenderFrameIdentical; + void ExecuteCommand() noexcept; -extern u16 RenderXPos; + s32 CyclesToRunFor() const noexcept; + void Run() noexcept; + void CheckFIFOIRQ() noexcept; + void CheckFIFODMA() noexcept; -extern std::array RenderPolygonRAM; -extern u32 RenderNumPolygons; + void VCount144(GPU& gpu) noexcept; + void VBlank() noexcept; + void VCount215(GPU& gpu) noexcept; -extern bool AbortFrame; + void RestartFrame(GPU& gpu) noexcept; + void Stop(const GPU& gpu) noexcept; -extern u64 Timestamp; + void SetRenderXPos(u16 xpos) noexcept; + [[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; } + u32* GetLine(int line) noexcept; -bool Init(); -void DeInit(); -void Reset(); + void WriteToGXFIFO(u32 val) noexcept; -void DoSavestate(Savestate* file); + [[nodiscard]] bool IsRendererAccelerated() const noexcept; + [[nodiscard]] Renderer3D& GetCurrentRenderer() noexcept { return *CurrentRenderer; } + [[nodiscard]] const Renderer3D& GetCurrentRenderer() const noexcept { return *CurrentRenderer; } + void SetCurrentRenderer(std::unique_ptr&& renderer) noexcept; -void SetEnabled(bool geometry, bool rendering); + u8 Read8(u32 addr) noexcept; + u16 Read16(u32 addr) noexcept; + u32 Read32(u32 addr) noexcept; + void Write8(u32 addr, u8 val) noexcept; + void Write16(u32 addr, u16 val) noexcept; + void Write32(u32 addr, u32 val) noexcept; + void Blit(const GPU& gpu) noexcept; +private: + melonDS::NDS& NDS; + typedef union + { + u64 _contents; + struct + { + u32 Param; + u8 Command; + }; -void ExecuteCommand(); + } CmdFIFOEntry; -s32 CyclesToRunFor(); -void Run(); -void CheckFIFOIRQ(); -void CheckFIFODMA(); + void UpdateClipMatrix() noexcept; + void ResetRenderingState() noexcept; + void AddCycles(s32 num) noexcept; + void NextVertexSlot() noexcept; + void StallPolygonPipeline(s32 delay, s32 nonstalldelay) noexcept; + void SubmitPolygon() noexcept; + void SubmitVertex() noexcept; + void CalculateLighting() noexcept; + void BoxTest(const u32* params) noexcept; + void PosTest() noexcept; + void VecTest(u32 param) noexcept; + void CmdFIFOWrite(const CmdFIFOEntry& entry) noexcept; + CmdFIFOEntry CmdFIFORead() noexcept; + void FinishWork(s32 cycles) noexcept; + void VertexPipelineSubmitCmd() noexcept + { + // vertex commands 0x24, 0x25, 0x26, 0x27, 0x28 + if (!(VertexSlotsFree & 0x1)) NextVertexSlot(); + else AddCycles(1); + NormalPipeline = 0; + } -void VCount144(); -void VBlank(); -void VCount215(); + void VertexPipelineCmdDelayed6() noexcept + { + // commands 0x20, 0x30, 0x31, 0x72 that can run 6 cycles after a vertex + if (VertexPipeline > 2) AddCycles((VertexPipeline - 2) + 1); + else AddCycles(NormalPipeline + 1); + NormalPipeline = 0; + } -void RestartFrame(); + void VertexPipelineCmdDelayed8() noexcept + { + // commands 0x29, 0x2A, 0x2B, 0x33, 0x34, 0x41, 0x60, 0x71 that can run 8 cycles after a vertex + if (VertexPipeline > 0) AddCycles(VertexPipeline + 1); + else AddCycles(NormalPipeline + 1); + NormalPipeline = 0; + } -void SetRenderXPos(u16 xpos); -u32* GetLine(int line); + void VertexPipelineCmdDelayed4() noexcept + { + // all other commands can run 4 cycles after a vertex + // no need to do much here since that is the minimum + AddCycles(NormalPipeline + 1); + NormalPipeline = 0; + } -void WriteToGXFIFO(u32 val); + std::unique_ptr CurrentRenderer = nullptr; -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); + u16 RenderXPos = 0; + +public: + FIFO CmdFIFO {}; + FIFO CmdPIPE {}; + + FIFO CmdStallQueue {}; + + u32 ZeroDotWLimit = 0; + + u32 GXStat = 0; + + u32 ExecParams[32] {}; + u32 ExecParamCount = 0; + + s32 CycleCount = 0; + s32 VertexPipeline = 0; + s32 NormalPipeline = 0; + s32 PolygonPipeline = 0; + s32 VertexSlotCounter = 0; + u32 VertexSlotsFree = 0; + + u32 NumPushPopCommands = 0; + u32 NumTestCommands = 0; + + + u32 MatrixMode = 0; + + s32 ProjMatrix[16] {}; + s32 PosMatrix[16] {}; + s32 VecMatrix[16] {}; + s32 TexMatrix[16] {}; + + s32 ClipMatrix[16] {}; + bool ClipMatrixDirty = false; + + u32 Viewport[6] {}; + + s32 ProjMatrixStack[16] {}; + s32 PosMatrixStack[32][16] {}; + s32 VecMatrixStack[32][16] {}; + s32 TexMatrixStack[16] {}; + s32 ProjMatrixStackPointer = 0; + s32 PosMatrixStackPointer = 0; + s32 TexMatrixStackPointer = 0; + + u32 NumCommands = 0; + u32 CurCommand = 0; + u32 ParamCount = 0; + u32 TotalParams = 0; + + bool GeometryEnabled = false; + bool RenderingEnabled = false; + + u32 DispCnt = 0; + u8 AlphaRefVal = 0; + u8 AlphaRef = 0; + + u16 ToonTable[32] {}; + u16 EdgeTable[8] {}; + + u32 FogColor = 0; + u32 FogOffset = 0; + u8 FogDensityTable[32] {}; + + u32 ClearAttr1 = 0; + u32 ClearAttr2 = 0; + + u32 RenderDispCnt = 0; + u8 RenderAlphaRef = 0; + + u16 RenderToonTable[32] {}; + u16 RenderEdgeTable[8] {}; + + u32 RenderFogColor = 0; + u32 RenderFogOffset = 0; + u32 RenderFogShift = 0; + u8 RenderFogDensityTable[34] {}; + + u32 RenderClearAttr1 = 0; + u32 RenderClearAttr2 = 0; + + bool RenderFrameIdentical = false; // not part of the hardware state, don't serialize + + bool AbortFrame = false; + + u64 Timestamp = 0; + + + u32 PolygonMode = 0; + s16 CurVertex[3] {}; + u8 VertexColor[3] {}; + s16 TexCoords[2] {}; + s16 RawTexCoords[2] {}; + s16 Normal[3] {}; + + s16 LightDirection[4][3] {}; + u8 LightColor[4][3] {}; + u8 MatDiffuse[3] {}; + u8 MatAmbient[3] {}; + u8 MatSpecular[3] {}; + u8 MatEmission[3] {}; + + bool UseShininessTable = false; + u8 ShininessTable[128] {}; + + u32 PolygonAttr = 0; + u32 CurPolygonAttr = 0; + + u32 TexParam = 0; + u32 TexPalette = 0; + + s32 PosTestResult[4] {}; + s16 VecTestResult[3] {}; + + Vertex TempVertexBuffer[4] {}; + u32 VertexNum = 0; + u32 VertexNumInPoly = 0; + u32 NumConsecutivePolygons = 0; + Polygon* LastStripPolygon = nullptr; + u32 NumOpaquePolygons = 0; + + Vertex VertexRAM[6144 * 2] {}; + Polygon PolygonRAM[2048 * 2] {}; + + Vertex* CurVertexRAM = nullptr; + Polygon* CurPolygonRAM = nullptr; + u32 NumVertices = 0; + u32 NumPolygons = 0; + u32 CurRAMBank = 0; + + std::array RenderPolygonRAM {}; + u32 RenderNumPolygons = 0; + + u32 FlushRequest = 0; + u32 FlushAttributes = 0; + u32 ScrolledLine[256]; // not part of the hardware state, don't serialize +}; class Renderer3D { @@ -142,20 +336,19 @@ public: Renderer3D(const Renderer3D&) = delete; Renderer3D& operator=(const Renderer3D&) = delete; - virtual void Reset() = 0; + virtual void Reset(GPU& gpu) = 0; // This "Accelerated" flag currently communicates if the framebuffer should // be allocated differently and other little misc handlers. Ideally there - // are more detailed "traits" that we can ask of the Renderer3D type + // are more detailed "traits" that we can ask of the Renderer3D type const bool Accelerated; - virtual void SetRenderSettings(GPU::RenderSettings& settings) = 0; - - virtual void VCount144() {}; - - virtual void RenderFrame() = 0; - virtual void RestartFrame() {}; + virtual void VCount144(GPU& gpu) {}; + virtual void Stop(const GPU& gpu) {} + virtual void RenderFrame(GPU& gpu) = 0; + virtual void RestartFrame(GPU& gpu) {}; virtual u32* GetLine(int line) = 0; + virtual void Blit(const GPU& gpu) {}; virtual void SetupAccelFrame() {} virtual void PrepareCaptureFrame() {} diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 825a4711..122b82ea 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -25,13 +25,13 @@ #include "GPU.h" #include "GPU3D_OpenGL_shaders.h" -namespace GPU3D +namespace melonDS { bool GLRenderer::BuildRenderShader(u32 flags, const std::string& vs, const std::string& fs) { char shadername[32]; - sprintf(shadername, "RenderShader%02X", flags); + snprintf(shadername, sizeof(shadername), "RenderShader%02X", flags); int headerlen = strlen(kShaderHeader); @@ -85,7 +85,9 @@ void SetupDefaultTexParams(GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } -GLRenderer::GLRenderer() noexcept : Renderer3D(true) +GLRenderer::GLRenderer(GLCompositor&& compositor) noexcept : + Renderer3D(true), + CurGLCompositor(std::move(compositor)) { // GLRenderer::New() will be used to actually initialize the renderer; // The various glDelete* functions silently ignore invalid IDs, @@ -96,9 +98,14 @@ std::unique_ptr GLRenderer::New() noexcept { assert(glEnable != nullptr); + std::optional compositor = GLCompositor::New(); + if (!compositor) + return nullptr; + // Will be returned if the initialization succeeds, // or cleaned up via RAII if it fails. - std::unique_ptr result = std::unique_ptr(new GLRenderer()); + std::unique_ptr result = std::unique_ptr(new GLRenderer(std::move(*compositor))); + compositor = std::nullopt; glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); @@ -303,16 +310,31 @@ GLRenderer::~GLRenderer() } } -void GLRenderer::Reset() +void GLRenderer::Reset(GPU& gpu) { + // This is where the compositor's Reset() method would be called, + // except there's no such method right now. } -void GLRenderer::SetRenderSettings(GPU::RenderSettings& settings) +void GLRenderer::SetBetterPolygons(bool betterpolygons) noexcept { - int scale = settings.GL_ScaleFactor; + SetRenderSettings(betterpolygons, ScaleFactor); +} +void GLRenderer::SetScaleFactor(int scale) noexcept +{ + SetRenderSettings(BetterPolygons, scale); +} + + +void GLRenderer::SetRenderSettings(bool betterpolygons, int scale) noexcept +{ + if (betterpolygons == BetterPolygons && scale == ScaleFactor) + return; + + CurGLCompositor.SetScaleFactor(scale); ScaleFactor = scale; - BetterPolygons = settings.GL_BetterPolygons; + BetterPolygons = betterpolygons; ScreenW = 256 * scale; ScreenH = 192 * scale; @@ -346,7 +368,7 @@ void GLRenderer::SetRenderSettings(GPU::RenderSettings& settings) } -void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) +void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) const { rp->PolyData = polygon; @@ -392,7 +414,7 @@ void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) } } -u32* GLRenderer::SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr) +u32* GLRenderer::SetupVertex(const Polygon* poly, int vid, const Vertex* vtx, u32 vtxattr, u32* vptr) const { u32 z = poly->FinalZ[vid]; u32 w = poly->FinalW[vid]; @@ -675,18 +697,18 @@ void GLRenderer::BuildPolygons(GLRenderer::RendererPolygon* polygons, int npolys NumEdgeIndices = eidx - EdgeIndicesOffset; } -int GLRenderer::RenderSinglePolygon(int i) +int GLRenderer::RenderSinglePolygon(int i) const { - RendererPolygon* rp = &PolygonList[i]; + const RendererPolygon* rp = &PolygonList[i]; glDrawElements(rp->PrimType, rp->NumIndices, GL_UNSIGNED_SHORT, (void*)(uintptr_t)(rp->IndicesOffset * 2)); return 1; } -int GLRenderer::RenderPolygonBatch(int i) +int GLRenderer::RenderPolygonBatch(int i) const { - RendererPolygon* rp = &PolygonList[i]; + const RendererPolygon* rp = &PolygonList[i]; GLuint primtype = rp->PrimType; u32 key = rp->RenderKey; int numpolys = 0; @@ -694,7 +716,7 @@ int GLRenderer::RenderPolygonBatch(int i) for (int iend = i; iend < NumFinalPolys; iend++) { - RendererPolygon* cur_rp = &PolygonList[iend]; + const RendererPolygon* cur_rp = &PolygonList[iend]; if (cur_rp->PrimType != primtype) break; if (cur_rp->RenderKey != key) break; @@ -706,16 +728,16 @@ int GLRenderer::RenderPolygonBatch(int i) return numpolys; } -int GLRenderer::RenderPolygonEdgeBatch(int i) +int GLRenderer::RenderPolygonEdgeBatch(int i) const { - RendererPolygon* rp = &PolygonList[i]; + const RendererPolygon* rp = &PolygonList[i]; u32 key = rp->RenderKey; int numpolys = 0; u32 numindices = 0; for (int iend = i; iend < NumFinalPolys; iend++) { - RendererPolygon* cur_rp = &PolygonList[iend]; + const RendererPolygon* cur_rp = &PolygonList[iend]; if (cur_rp->RenderKey != key) break; numpolys++; @@ -726,14 +748,14 @@ int GLRenderer::RenderPolygonEdgeBatch(int i) return numpolys; } -void GLRenderer::RenderSceneChunk(int y, int h) +void GLRenderer::RenderSceneChunk(const GPU3D& gpu3d, int y, int h) { u32 flags = 0; - if (RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; + if (gpu3d.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; if (h != 192) glScissor(0, y<PolyData->IsShadow) { // shadow against clear-plane will only pass if its polyID matches that of the clear plane - u32 clrpolyid = (RenderClearAttr1 >> 24) & 0x3F; + u32 clrpolyid = (gpu3d.RenderClearAttr1 >> 24) & 0x3F; if (polyid != clrpolyid) { i++; continue; } glEnable(GL_BLEND); @@ -1029,7 +1051,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) } } - if (RenderDispCnt & 0x00A0) // fog/edge enabled + if (gpu3d.RenderDispCnt & 0x00A0) // fog/edge enabled { glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMaski(1, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); @@ -1051,7 +1073,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID); glBindVertexArray(ClearVertexArrayID); - if (RenderDispCnt & (1<<5)) + if (gpu3d.RenderDispCnt & (1<<5)) { // edge marking // TODO: depth/polyid values at screen edges @@ -1063,19 +1085,19 @@ void GLRenderer::RenderSceneChunk(int y, int h) glDrawArrays(GL_TRIANGLES, 0, 2*3); } - if (RenderDispCnt & (1<<7)) + if (gpu3d.RenderDispCnt & (1<<7)) { // fog glUseProgram(FinalPassFogShader); - if (RenderDispCnt & (1<<6)) + if (gpu3d.RenderDispCnt & (1<<6)) glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); else glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); { - u32 c = RenderFogColor; + u32 c = gpu3d.RenderFogColor; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1090,7 +1112,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) } -void GLRenderer::RenderFrame() +void GLRenderer::RenderFrame(GPU& gpu) { CurShaderID = -1; @@ -1099,11 +1121,11 @@ void GLRenderer::RenderFrame() ShaderConfig.uScreenSize[0] = ScreenW; ShaderConfig.uScreenSize[1] = ScreenH; - ShaderConfig.uDispCnt = RenderDispCnt; + ShaderConfig.uDispCnt = gpu.GPU3D.RenderDispCnt; for (int i = 0; i < 32; i++) { - u16 c = RenderToonTable[i]; + u16 c = gpu.GPU3D.RenderToonTable[i]; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1115,7 +1137,7 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 8; i++) { - u16 c = RenderEdgeTable[i]; + u16 c = gpu.GPU3D.RenderEdgeTable[i]; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1126,7 +1148,7 @@ void GLRenderer::RenderFrame() } { - u32 c = RenderFogColor; + u32 c = gpu.GPU3D.RenderFogColor; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1140,12 +1162,12 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 34; i++) { - u8 d = RenderFogDensityTable[i]; + u8 d = gpu.GPU3D.RenderFogDensityTable[i]; ShaderConfig.uFogDensity[i][0] = (float)d / 127.0; } - ShaderConfig.uFogOffset = RenderFogOffset; - ShaderConfig.uFogShift = RenderFogShift; + ShaderConfig.uFogOffset = gpu.GPU3D.RenderFogOffset; + ShaderConfig.uFogShift = gpu.GPU3D.RenderFogShift; glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO); void* unibuf = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY); @@ -1158,13 +1180,13 @@ void GLRenderer::RenderFrame() glBindTexture(GL_TEXTURE_2D, TexMemID); for (int i = 0; i < 4; i++) { - u32 mask = GPU::VRAMMap_Texture[i]; + u32 mask = gpu.VRAMMap_Texture[i]; u8* vram; if (!mask) continue; - else if (mask & (1<<0)) vram = GPU::VRAM_A; - else if (mask & (1<<1)) vram = GPU::VRAM_B; - else if (mask & (1<<2)) vram = GPU::VRAM_C; - else if (mask & (1<<3)) vram = GPU::VRAM_D; + else if (mask & (1<<0)) vram = gpu.VRAM_A; + else if (mask & (1<<1)) vram = gpu.VRAM_B; + else if (mask & (1<<2)) vram = gpu.VRAM_C; + else if (mask & (1<<3)) vram = gpu.VRAM_D; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*128, 1024, 128, GL_RED_INTEGER, GL_UNSIGNED_BYTE, vram); } @@ -1174,12 +1196,12 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 6; i++) { // 6 x 16K chunks - u32 mask = GPU::VRAMMap_TexPal[i]; + u32 mask = gpu.VRAMMap_TexPal[i]; u8* vram; if (!mask) continue; - else if (mask & (1<<4)) vram = &GPU::VRAM_E[(i&3)*0x4000]; - else if (mask & (1<<5)) vram = GPU::VRAM_F; - else if (mask & (1<<6)) vram = GPU::VRAM_G; + else if (mask & (1<<4)) vram = &gpu.VRAM_E[(i&3)*0x4000]; + else if (mask & (1<<5)) vram = gpu.VRAM_F; + else if (mask & (1<<6)) vram = gpu.VRAM_G; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*8, 1024, 8, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, vram); } @@ -1204,13 +1226,13 @@ void GLRenderer::RenderFrame() glUseProgram(ClearShaderPlain); glDepthFunc(GL_ALWAYS); - u32 r = RenderClearAttr1 & 0x1F; - u32 g = (RenderClearAttr1 >> 5) & 0x1F; - u32 b = (RenderClearAttr1 >> 10) & 0x1F; - u32 fog = (RenderClearAttr1 >> 15) & 0x1; - u32 a = (RenderClearAttr1 >> 16) & 0x1F; - u32 polyid = (RenderClearAttr1 >> 24) & 0x3F; - u32 z = ((RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + u32 r = gpu.GPU3D.RenderClearAttr1 & 0x1F; + u32 g = (gpu.GPU3D.RenderClearAttr1 >> 5) & 0x1F; + u32 b = (gpu.GPU3D.RenderClearAttr1 >> 10) & 0x1F; + u32 fog = (gpu.GPU3D.RenderClearAttr1 >> 15) & 0x1; + u32 a = (gpu.GPU3D.RenderClearAttr1 >> 16) & 0x1F; + u32 polyid = (gpu.GPU3D.RenderClearAttr1 >> 24) & 0x3F; + u32 z = ((gpu.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; glStencilFunc(GL_ALWAYS, 0xFF, 0xFF); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); @@ -1229,20 +1251,20 @@ void GLRenderer::RenderFrame() glDrawArrays(GL_TRIANGLES, 0, 2*3); } - if (RenderNumPolygons) + if (gpu.GPU3D.RenderNumPolygons) { // render shit here u32 flags = 0; - if (RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; + if (gpu.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; int npolys = 0; int firsttrans = -1; - for (u32 i = 0; i < RenderNumPolygons; i++) + for (u32 i = 0; i < gpu.GPU3D.RenderNumPolygons; i++) { - if (RenderPolygonRAM[i]->Degenerate) continue; + if (gpu.GPU3D.RenderPolygonRAM[i]->Degenerate) continue; - SetupPolygon(&PolygonList[npolys], RenderPolygonRAM[i]); - if (firsttrans < 0 && RenderPolygonRAM[i]->Translucent) + SetupPolygon(&PolygonList[npolys], gpu.GPU3D.RenderPolygonRAM[i]); + if (firsttrans < 0 && gpu.GPU3D.RenderPolygonRAM[i]->Translucent) firsttrans = npolys; npolys++; @@ -1259,10 +1281,15 @@ void GLRenderer::RenderFrame() glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, NumIndices * 2, IndexBuffer); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, EdgeIndicesOffset * 2, NumEdgeIndices * 2, IndexBuffer + EdgeIndicesOffset); - RenderSceneChunk(0, 192); + RenderSceneChunk(gpu.GPU3D, 0, 192); } } +void GLRenderer::Stop(const GPU& gpu) +{ + CurGLCompositor.Stop(gpu); +} + void GLRenderer::PrepareCaptureFrame() { glBindFramebuffer(GL_READ_FRAMEBUFFER, MainFramebuffer); @@ -1276,6 +1303,11 @@ void GLRenderer::PrepareCaptureFrame() glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL); } +void GLRenderer::Blit(const GPU& gpu) +{ + CurGLCompositor.RenderFrame(gpu, *this); +} + u32* GLRenderer::GetLine(int line) { int stride = 256; diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index e487e844..cc9956f7 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -18,32 +18,43 @@ #pragma once +#ifdef OGLRENDERER_ENABLED #include "GPU3D.h" - +#include "GPU_OpenGL.h" #include "OpenGLSupport.h" - -namespace GPU3D +namespace melonDS { +class GPU; + class GLRenderer : public Renderer3D { public: - virtual ~GLRenderer() override; - virtual void Reset() override; + ~GLRenderer() override; + void Reset(GPU& gpu) override; - virtual void SetRenderSettings(GPU::RenderSettings& settings) override; + void SetRenderSettings(bool betterpolygons, int scale) noexcept; + void SetBetterPolygons(bool betterpolygons) noexcept; + void SetScaleFactor(int scale) noexcept; + [[nodiscard]] bool GetBetterPolygons() const noexcept { return BetterPolygons; } + [[nodiscard]] int GetScaleFactor() const noexcept { return ScaleFactor; } - virtual void VCount144() override {}; - virtual void RenderFrame() override; - virtual u32* GetLine(int line) override; + void VCount144(GPU& gpu) override {}; + void RenderFrame(GPU& gpu) override; + void Stop(const GPU& gpu) override; + u32* GetLine(int line) override; void SetupAccelFrame() override; - void PrepareCaptureFrame() override; + void PrepareCaptureFrame() override override; + void Blit(const GPU& gpu) override; + + [[nodiscard]] const GLCompositor& GetCompositor() const noexcept { return CurGLCompositor; } + GLCompositor& GetCompositor() noexcept { return CurGLCompositor; } static std::unique_ptr New() noexcept; private: // Used by New() - GLRenderer() noexcept; + GLRenderer(GLCompositor&& compositor) noexcept; // GL version requirements // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) @@ -63,17 +74,18 @@ private: u32 RenderKey; }; + GLCompositor CurGLCompositor; RendererPolygon PolygonList[2048] {}; bool BuildRenderShader(u32 flags, const std::string& vs, const std::string& fs); void UseRenderShader(u32 flags); - void SetupPolygon(RendererPolygon* rp, Polygon* polygon); - u32* SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr); + void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const; + u32* SetupVertex(const Polygon* poly, int vid, const Vertex* vtx, u32 vtxattr, u32* vptr) const; void BuildPolygons(RendererPolygon* polygons, int npolys); - int RenderSinglePolygon(int i); - int RenderPolygonBatch(int i); - int RenderPolygonEdgeBatch(int i); - void RenderSceneChunk(int y, int h); + int RenderSinglePolygon(int i) const; + int RenderPolygonBatch(int i) const; + int RenderPolygonEdgeBatch(int i) const; + void RenderSceneChunk(const GPU3D& gpu3d, int y, int h); enum { @@ -150,4 +162,5 @@ private: GLuint MainFramebuffer {}, DownscaleFramebuffer {}; u32 Framebuffer[256*192] {}; }; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h index 6e17c350..13492b7f 100644 --- a/src/GPU3D_OpenGL_shaders.h +++ b/src/GPU3D_OpenGL_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,7 +21,8 @@ #define kShaderHeader "#version 140" - +namespace melonDS +{ const char* kClearVS = kShaderHeader R"( in vec2 vPosition; @@ -802,5 +803,5 @@ void main() gl_FragDepth = fZ; } )"; - +} #endif // GPU3D_OPENGL_SHADERS_H diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 8848a541..74027d5b 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,8 +24,7 @@ #include "NDS.h" #include "GPU.h" - -namespace GPU3D +namespace melonDS { void RenderThreadFunc(); @@ -35,35 +34,52 @@ void SoftRenderer::StopRenderThread() { if (RenderThreadRunning.load(std::memory_order_relaxed)) { + // Tell the render thread to stop drawing new frames, and finish up the current one. RenderThreadRunning = false; + Platform::Semaphore_Post(Sema_RenderStart); + Platform::Thread_Wait(RenderThread); Platform::Thread_Free(RenderThread); RenderThread = nullptr; } } -void SoftRenderer::SetupRenderThread() +void SoftRenderer::SetupRenderThread(GPU& gpu) { if (Threaded) { if (!RenderThreadRunning.load(std::memory_order_relaxed)) - { - RenderThreadRunning = true; - RenderThread = Platform::Thread_Create(std::bind(&SoftRenderer::RenderThreadFunc, this)); + { // If the render thread isn't already running... + RenderThreadRunning = true; // "Time for work, render thread!" + RenderThread = Platform::Thread_Create([this, &gpu]() { + RenderThreadFunc(gpu); + }); } - // otherwise more than one frame can be queued up at once + // "Be on standby, but don't start rendering until I tell you to!" Platform::Semaphore_Reset(Sema_RenderStart); + // "Oh, sorry, were you already in the middle of a frame from the last iteration?" if (RenderThreadRendering) + // "Tell me when you're done, I'll wait here." Platform::Semaphore_Wait(Sema_RenderDone); - Platform::Semaphore_Reset(Sema_RenderDone); - Platform::Semaphore_Reset(Sema_RenderStart); - Platform::Semaphore_Reset(Sema_ScanlineCount); + // "All good? Okay, let me give you your training." + // "(Maybe you're still the same thread, but I have to tell you this stuff anyway.)" - Platform::Semaphore_Post(Sema_RenderStart); + // "This is the signal you'll send when you're done with a frame." + // "I'll listen for it when I need to show something to the frontend." + Platform::Semaphore_Reset(Sema_RenderDone); + + // "This is the signal I'll send when I want you to start rendering." + // "Don't do anything until you get the message." + Platform::Semaphore_Reset(Sema_RenderStart); + + // "This is the signal you'll send every time you finish drawing a line." + // "I might need some of your scanlines before you finish the whole buffer," + // "so let me know as soon as you're done with each one." + Platform::Semaphore_Reset(Sema_ScanlineCount); } else { @@ -71,15 +87,21 @@ void SoftRenderer::SetupRenderThread() } } +void SoftRenderer::EnableRenderThread() +{ + if (Threaded && Sema_RenderStart) + { + Platform::Semaphore_Post(Sema_RenderStart); + } +} -SoftRenderer::SoftRenderer() noexcept - : Renderer3D(false) +SoftRenderer::SoftRenderer(bool threaded) noexcept + : Renderer3D(false), Threaded(threaded) { Sema_RenderStart = Platform::Semaphore_Create(); Sema_RenderDone = Platform::Semaphore_Create(); Sema_ScanlineCount = Platform::Semaphore_Create(); - Threaded = false; RenderThreadRunning = false; RenderThreadRendering = false; RenderThread = nullptr; @@ -94,7 +116,7 @@ SoftRenderer::~SoftRenderer() Platform::Semaphore_Free(Sema_ScanlineCount); } -void SoftRenderer::Reset() +void SoftRenderer::Reset(GPU& gpu) { memset(ColorBuffer, 0, BufferSize * 2 * 4); memset(DepthBuffer, 0, BufferSize * 2 * 4); @@ -102,16 +124,21 @@ void SoftRenderer::Reset() PrevIsShadowMask = false; - SetupRenderThread(); + SetupRenderThread(gpu); + EnableRenderThread(); } -void SoftRenderer::SetRenderSettings(GPU::RenderSettings& settings) +void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept { - Threaded = settings.Soft_Threaded; - SetupRenderThread(); + if (Threaded != threaded) + { + Threaded = threaded; + SetupRenderThread(gpu); + EnableRenderThread(); + } } -void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) +void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const { u32 vramaddr = (texparam & 0xFFFF) << 3; @@ -166,10 +193,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 1: // A3I5 { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + ((pixel&0x1F)<<1)); + *color = ReadVRAM_TexPal(texpal + ((pixel&0x1F)<<1), gpu); *alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6); } break; @@ -177,12 +204,12 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 2: // 4-color { vramaddr += (((t * width) + s) >> 2); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); pixel >>= ((s & 0x3) << 1); pixel &= 0x3; texpal <<= 3; - *color = ReadVRAM_TexPal(texpal + (pixel<<1)); + *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -190,12 +217,12 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 3: // 16-color { vramaddr += (((t * width) + s) >> 1); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); if (s & 0x1) pixel >>= 4; else pixel &= 0xF; texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + (pixel<<1)); + *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -203,10 +230,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 4: // 256-color { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + (pixel<<1)); + *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -220,30 +247,30 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co if (vramaddr >= 0x40000) slot1addr += 0x10000; - u8 val = ReadVRAM_Texture(vramaddr); + u8 val = ReadVRAM_Texture(vramaddr, gpu); val >>= (2 * (s & 0x3)); - u16 palinfo = ReadVRAM_Texture(slot1addr); + u16 palinfo = ReadVRAM_Texture(slot1addr, gpu); u32 paloffset = (palinfo & 0x3FFF) << 2; texpal <<= 4; switch (val & 0x3) { case 0: - *color = ReadVRAM_TexPal(texpal + paloffset); + *color = ReadVRAM_TexPal(texpal + paloffset, gpu); *alpha = 31; break; case 1: - *color = ReadVRAM_TexPal(texpal + paloffset + 2); + *color = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); *alpha = 31; break; case 2: if ((palinfo >> 14) == 1) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2); + u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); + u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -260,8 +287,8 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co } else if ((palinfo >> 14) == 3) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2); + u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); + u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -277,20 +304,20 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co *color = r | g | b; } else - *color = ReadVRAM_TexPal(texpal + paloffset + 4); + *color = ReadVRAM_TexPal(texpal + paloffset + 4, gpu); *alpha = 31; break; case 3: if ((palinfo >> 14) == 2) { - *color = ReadVRAM_TexPal(texpal + paloffset + 6); + *color = ReadVRAM_TexPal(texpal + paloffset + 6, gpu); *alpha = 31; } else if ((palinfo >> 14) == 3) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2); + u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); + u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -319,10 +346,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 6: // A5I3 { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + ((pixel&0x7)<<1)); + *color = ReadVRAM_TexPal(texpal + ((pixel&0x7)<<1), gpu); *alpha = (pixel >> 3); } break; @@ -330,7 +357,7 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 7: // direct color { vramaddr += (((t * width) + s) << 1); - *color = ReadVRAM_Texture(vramaddr); + *color = ReadVRAM_Texture(vramaddr, gpu); *alpha = (*color & 0x8000) ? 31 : 0; } break; @@ -387,7 +414,7 @@ bool DepthTest_LessThan_FrontFacing(s32 dstz, s32 z, u32 dstattr) return false; } -u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) +u32 SoftRenderer::AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept { u32 dstalpha = dstcolor >> 24; @@ -398,7 +425,7 @@ u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) u32 srcG = (srccolor >> 8) & 0x3F; u32 srcB = (srccolor >> 16) & 0x3F; - if (RenderDispCnt & (1<<3)) + if (gpu3d.RenderDispCnt & (1<<3)) { u32 dstR = dstcolor & 0x3F; u32 dstG = (dstcolor >> 8) & 0x3F; @@ -417,7 +444,7 @@ u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) return srcR | (srcG << 8) | (srcB << 16) | (dstalpha << 24); } -u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) +u32 SoftRenderer::RenderPixel(const GPU& gpu, const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const { u8 r, g, b, a; @@ -427,7 +454,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 if (blendmode == 2) { - if (RenderDispCnt & (1<<1)) + if (gpu.GPU3D.RenderDispCnt & (1<<1)) { // highlight mode: color is calculated normally // except all vertex color components are set @@ -441,7 +468,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 { // toon mode: vertex color is replaced by toon color - u16 tooncolor = RenderToonTable[vr >> 1]; + u16 tooncolor = gpu.GPU3D.RenderToonTable[vr >> 1]; vr = (tooncolor << 1) & 0x3E; if (vr) vr++; vg = (tooncolor >> 4) & 0x3E; if (vg) vg++; @@ -449,12 +476,12 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 } } - if ((RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0)) + if ((gpu.GPU3D.RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0)) { u8 tr, tg, tb; u16 tcolor; u8 talpha; - TextureLookup(polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha); + TextureLookup(gpu, polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha); tr = (tcolor << 1) & 0x3E; if (tr) tr++; tg = (tcolor >> 4) & 0x3E; if (tg) tg++; @@ -502,9 +529,9 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 a = polyalpha; } - if ((blendmode == 2) && (RenderDispCnt & (1<<1))) + if ((blendmode == 2) && (gpu.GPU3D.RenderDispCnt & (1<<1))) { - u16 tooncolor = RenderToonTable[vr >> 1]; + u16 tooncolor = gpu.GPU3D.RenderToonTable[vr >> 1]; vr = (tooncolor << 1) & 0x3E; if (vr) vr++; vg = (tooncolor >> 4) & 0x3E; if (vg) vg++; @@ -525,7 +552,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 return r | (g << 8) | (b << 16) | (a << 24); } -void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow) +void SoftRenderer::PlotTranslucentPixel(const GPU3D& gpu3d, u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow) { u32 dstattr = AttrBuffer[pixeladdr]; u32 attr = (polyattr & 0xE0F0) | ((polyattr >> 8) & 0xFF0000) | (1<<22) | (dstattr & 0xFF001F0F); @@ -555,7 +582,7 @@ void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 pol if (!(dstattr & (1<<15))) attr &= ~(1<<15); - color = AlphaBlend(color, ColorBuffer[pixeladdr], color>>24); + color = AlphaBlend(gpu3d, color, ColorBuffer[pixeladdr], color>>24); if (z != -1) DepthBuffer[pixeladdr] = z; @@ -564,7 +591,7 @@ void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 pol AttrBuffer[pixeladdr] = attr; } -void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y) +void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y) const { Polygon* polygon = rp->PolyData; @@ -591,7 +618,7 @@ void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL], y); } -void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y) +void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y) const { Polygon* polygon = rp->PolyData; @@ -618,7 +645,7 @@ void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR], y); } -void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon) +void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon) const { u32 nverts = polygon->NumVertices; @@ -671,7 +698,7 @@ void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* poly } } -void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) +void SoftRenderer::RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon* rp, s32 y) { Polygon* polygon = rp->PolyData; @@ -746,9 +773,9 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) std::swap(xstart, xend); std::swap(wl, wr); std::swap(zl, zr); - + // CHECKME: edge fill rules for swapped opaque shadow mask polygons - if ((polyalpha < 31) || (RenderDispCnt & (3<<4))) + if ((gpu3d.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu3d.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -774,9 +801,9 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->SlopeL.EdgeParams(&l_edgelen, &l_edgecov); rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov); - + // CHECKME: edge fill rules for unswapped opaque shadow mask polygons - if ((polyalpha < 31) || (RenderDispCnt & (3<<4))) + if ((gpu3d.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu3d.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -797,7 +824,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) // similarly, we can perform alpha test early (checkme) if (wireframe) polyalpha = 31; - if (polyalpha <= RenderAlphaRef) return; + if (polyalpha <= gpu3d.RenderAlphaRef) return; // in wireframe mode, there are special rules for equal Z (TODO) @@ -873,7 +900,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) edge = yedge | 0x2; xlimit = xend+1; if (xlimit > 256) xlimit = 256; - + if (r_filledge) for (; x < xlimit; x++) { @@ -899,7 +926,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->XR = rp->SlopeR.Step(); } -void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) +void SoftRenderer::RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s32 y) { Polygon* polygon = rp->PolyData; @@ -948,7 +975,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s32 zl = rp->SlopeL.Interp.InterpolateZ(polygon->FinalZ[rp->CurVL], polygon->FinalZ[rp->NextVL], polygon->WBuffer); s32 zr = rp->SlopeR.Interp.InterpolateZ(polygon->FinalZ[rp->CurVR], polygon->FinalZ[rp->NextVR], polygon->WBuffer); - + // right vertical edges are pushed 1px to the left as long as either: // the left edge slope is not 0, or the span is not 0 pixels wide, and it is not at the leftmost pixel of the screen if (rp->SlopeR.Increment==0 && (rp->SlopeL.Increment!=0 || xstart != xend) && (xend != 0)) @@ -980,9 +1007,10 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * right edge is filled if slope > 1, or if the left edge = 0, but is never filled if it is < -1 // * left edge is filled if slope <= 1 // * the bottom-most pixel of negative x-major slopes are filled if they are next to a flat bottom edge - // edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent + // edges are always filled if antialiasing/edgemarking are enabled, + // if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe // checkme: do swapped line polygons exist? - if ((polyalpha < 31) || wireframe || (RenderDispCnt & ((1<<4)|(1<<5)))) + if ((gpu.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -1015,8 +1043,9 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * edges with slope = 0 are always filled // * the bottom-most pixel of negative x-major slopes are filled if they are next to a flat bottom edge // * edges are filled if both sides are identical and fully overlapping - // edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent - if ((polyalpha < 31) || wireframe || (RenderDispCnt & ((1<<4)|(1<<5)))) + // edges are always filled if antialiasing/edgemarking are enabled, + // if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe + if ((gpu.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -1115,17 +1144,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s16 s = interpX.Interpolate(sl, sr); s16 t = interpX.Interpolate(tl, tr); - u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t); + u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t); u8 alpha = color >> 24; // alpha test - if (alpha <= RenderAlphaRef) continue; + if (alpha <= gpu.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if (RenderDispCnt & (1<<4)) + if (gpu.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing: all edges are rendered @@ -1155,11 +1184,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) else { if (!(polygon->Attr & (1<<11))) z = -1; - PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow); // blend with bottom pixel too, if needed if ((dstattr & 0xF) && (pixeladdr < BufferSize)) - PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); } } @@ -1211,17 +1240,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s16 s = interpX.Interpolate(sl, sr); s16 t = interpX.Interpolate(tl, tr); - u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t); + u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t); u8 alpha = color >> 24; // alpha test - if (alpha <= RenderAlphaRef) continue; + if (alpha <= gpu.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - - if ((RenderDispCnt & (1<<4)) && (attr & 0xF)) + + if ((gpu.GPU3D.RenderDispCnt & (1<<4)) && (attr & 0xF)) { // anti-aliasing: all edges are rendered @@ -1244,11 +1273,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) else { if (!(polygon->Attr & (1<<11))) z = -1; - PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow); // blend with bottom pixel too, if needed if ((dstattr & 0xF) && (pixeladdr < BufferSize)) - PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); } } @@ -1303,17 +1332,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s16 s = interpX.Interpolate(sl, sr); s16 t = interpX.Interpolate(tl, tr); - u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t); + u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t); u8 alpha = color >> 24; // alpha test - if (alpha <= RenderAlphaRef) continue; + if (alpha <= gpu.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if (RenderDispCnt & (1<<4)) + if (gpu.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing: all edges are rendered @@ -1343,11 +1372,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) else { if (!(polygon->Attr & (1<<11))) z = -1; - PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow); // blend with bottom pixel too, if needed if ((dstattr & 0xF) && (pixeladdr < BufferSize)) - PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); } } @@ -1355,7 +1384,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) rp->XR = rp->SlopeR.Step(); } -void SoftRenderer::RenderScanline(s32 y, int npolys) +void SoftRenderer::RenderScanline(const GPU& gpu, s32 y, int npolys) { for (int i = 0; i < npolys; i++) { @@ -1365,19 +1394,19 @@ void SoftRenderer::RenderScanline(s32 y, int npolys) if (y >= polygon->YTop && (y < polygon->YBottom || (y == polygon->YTop && polygon->YBottom == polygon->YTop))) { if (polygon->IsShadowMask) - RenderShadowMaskScanline(rp, y); + RenderShadowMaskScanline(gpu.GPU3D, rp, y); else - RenderPolygonScanline(rp, y); + RenderPolygonScanline(gpu, rp, y); } } } -u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) +u32 SoftRenderer::CalculateFogDensity(const GPU3D& gpu3d, u32 pixeladdr) const { u32 z = DepthBuffer[pixeladdr]; u32 densityid, densityfrac; - if (z < RenderFogOffset) + if (z < gpu3d.RenderFogOffset) { densityid = 0; densityfrac = 0; @@ -1389,8 +1418,8 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) // on hardware, the final value can overflow the 32-bit range with a shift big enough, // causing fog to 'wrap around' and accidentally apply to larger Z ranges - z -= RenderFogOffset; - z = (z >> 2) << RenderFogShift; + z -= gpu3d.RenderFogOffset; + z = (z >> 2) << gpu3d.RenderFogShift; densityid = z >> 17; if (densityid >= 32) @@ -1404,20 +1433,20 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) // checkme (may be too precise?) u32 density = - ((RenderFogDensityTable[densityid] * (0x20000-densityfrac)) + - (RenderFogDensityTable[densityid+1] * densityfrac)) >> 17; + ((gpu3d.RenderFogDensityTable[densityid] * (0x20000-densityfrac)) + + (gpu3d.RenderFogDensityTable[densityid+1] * densityfrac)) >> 17; if (density >= 127) density = 128; return density; } -void SoftRenderer::ScanlineFinalPass(s32 y) +void SoftRenderer::ScanlineFinalPass(const GPU3D& gpu3d, s32 y) { // to consider: // clearing all polygon fog flags if the master flag isn't set? // merging all final pass loops into one? - if (RenderDispCnt & (1<<5)) + if (gpu3d.RenderDispCnt & (1<<5)) { // edge marking // only applied to topmost pixels @@ -1437,7 +1466,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) ((polyid != (AttrBuffer[pixeladdr-ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr-ScanlineWidth])) || ((polyid != (AttrBuffer[pixeladdr+ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr+ScanlineWidth]))) { - u16 edgecolor = RenderEdgeTable[polyid >> 3]; + u16 edgecolor = gpu3d.RenderEdgeTable[polyid >> 3]; u32 edgeR = (edgecolor << 1) & 0x3E; if (edgeR) edgeR++; u32 edgeG = (edgecolor >> 4) & 0x3E; if (edgeG) edgeG++; u32 edgeB = (edgecolor >> 9) & 0x3E; if (edgeB) edgeB++; @@ -1450,7 +1479,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } - if (RenderDispCnt & (1<<7)) + if (gpu3d.RenderDispCnt & (1<<7)) { // fog @@ -1463,12 +1492,12 @@ void SoftRenderer::ScanlineFinalPass(s32 y) // TODO: check the 'fog alpha glitch with small Z' GBAtek talks about - bool fogcolor = !(RenderDispCnt & (1<<6)); + bool fogcolor = !(gpu3d.RenderDispCnt & (1<<6)); - u32 fogR = (RenderFogColor << 1) & 0x3E; if (fogR) fogR++; - u32 fogG = (RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; - u32 fogB = (RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; - u32 fogA = (RenderFogColor >> 16) & 0x1F; + u32 fogR = (gpu3d.RenderFogColor << 1) & 0x3E; if (fogR) fogR++; + u32 fogG = (gpu3d.RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; + u32 fogB = (gpu3d.RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; + u32 fogA = (gpu3d.RenderFogColor >> 16) & 0x1F; for (int x = 0; x < 256; x++) { @@ -1478,7 +1507,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) u32 attr = AttrBuffer[pixeladdr]; if (attr & (1<<15)) { - density = CalculateFogDensity(pixeladdr); + density = CalculateFogDensity(gpu3d, pixeladdr); srccolor = ColorBuffer[pixeladdr]; srcR = srccolor & 0x3F; @@ -1507,7 +1536,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) attr = AttrBuffer[pixeladdr]; if (!(attr & (1<<15))) continue; - density = CalculateFogDensity(pixeladdr); + density = CalculateFogDensity(gpu3d, pixeladdr); srccolor = ColorBuffer[pixeladdr]; srcR = srccolor & 0x3F; @@ -1528,7 +1557,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } - if (RenderDispCnt & (1<<4)) + if (gpu3d.RenderDispCnt & (1<<4)) { // anti-aliasing @@ -1581,10 +1610,10 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } -void SoftRenderer::ClearBuffers() +void SoftRenderer::ClearBuffers(const GPU& gpu) { - u32 clearz = ((RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; - u32 polyid = RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID + u32 clearz = ((gpu.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + u32 polyid = gpu.GPU3D.RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID // fill screen borders for edge marking @@ -1614,17 +1643,17 @@ void SoftRenderer::ClearBuffers() // clear the screen - if (RenderDispCnt & (1<<14)) + if (gpu.GPU3D.RenderDispCnt & (1<<14)) { - u8 xoff = (RenderClearAttr2 >> 16) & 0xFF; - u8 yoff = (RenderClearAttr2 >> 24) & 0xFF; + u8 xoff = (gpu.GPU3D.RenderClearAttr2 >> 16) & 0xFF; + u8 yoff = (gpu.GPU3D.RenderClearAttr2 >> 24) & 0xFF; for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth) { for (int x = 0; x < 256; x++) { - u16 val2 = ReadVRAM_Texture(0x40000 + (yoff << 9) + (xoff << 1)); - u16 val3 = ReadVRAM_Texture(0x60000 + (yoff << 9) + (xoff << 1)); + u16 val2 = ReadVRAM_Texture(0x40000 + (yoff << 9) + (xoff << 1), gpu); + u16 val3 = ReadVRAM_Texture(0x60000 + (yoff << 9) + (xoff << 1), gpu); // TODO: confirm color conversion u32 r = (val2 << 1) & 0x3E; if (r) r++; @@ -1649,13 +1678,13 @@ void SoftRenderer::ClearBuffers() else { // TODO: confirm color conversion - u32 r = (RenderClearAttr1 << 1) & 0x3E; if (r) r++; - u32 g = (RenderClearAttr1 >> 4) & 0x3E; if (g) g++; - u32 b = (RenderClearAttr1 >> 9) & 0x3E; if (b) b++; - u32 a = (RenderClearAttr1 >> 16) & 0x1F; + u32 r = (gpu.GPU3D.RenderClearAttr1 << 1) & 0x3E; if (r) r++; + u32 g = (gpu.GPU3D.RenderClearAttr1 >> 4) & 0x3E; if (g) g++; + u32 b = (gpu.GPU3D.RenderClearAttr1 >> 9) & 0x3E; if (b) b++; + u32 a = (gpu.GPU3D.RenderClearAttr1 >> 16) & 0x1F; u32 color = r | (g << 8) | (b << 16) | (a << 24); - polyid |= (RenderClearAttr1 & 0x8000); + polyid |= (gpu.GPU3D.RenderClearAttr1 & 0x8000); for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth) { @@ -1670,7 +1699,7 @@ void SoftRenderer::ClearBuffers() } } -void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys) +void SoftRenderer::RenderPolygons(const GPU& gpu, bool threaded, Polygon** polygons, int npolys) { int j = 0; for (int i = 0; i < npolys; i++) @@ -1679,74 +1708,88 @@ void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys) SetupPolygon(&PolygonList[j++], polygons[i]); } - RenderScanline(0, j); + RenderScanline(gpu, 0, j); for (s32 y = 1; y < 192; y++) { - RenderScanline(y, j); - ScanlineFinalPass(y-1); + RenderScanline(gpu, y, j); + ScanlineFinalPass(gpu.GPU3D, y-1); if (threaded) + // Notify the main thread that we're done with a scanline. Platform::Semaphore_Post(Sema_ScanlineCount); } - ScanlineFinalPass(191); + ScanlineFinalPass(gpu.GPU3D, 191); if (threaded) + // If this renderer is threaded, notify the main thread that we're done with the frame. Platform::Semaphore_Post(Sema_ScanlineCount); } -void SoftRenderer::VCount144() +void SoftRenderer::VCount144(GPU& gpu) { - if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU3D::AbortFrame) + if (RenderThreadRunning.load(std::memory_order_relaxed) && !gpu.GPU3D.AbortFrame) Platform::Semaphore_Wait(Sema_RenderDone); } -void SoftRenderer::RenderFrame() +void SoftRenderer::RenderFrame(GPU& gpu) { - auto textureDirty = GPU::VRAMDirty_Texture.DeriveState(GPU::VRAMMap_Texture); - auto texPalDirty = GPU::VRAMDirty_TexPal.DeriveState(GPU::VRAMMap_TexPal); + auto textureDirty = gpu.VRAMDirty_Texture.DeriveState(gpu.VRAMMap_Texture, gpu); + auto texPalDirty = gpu.VRAMDirty_TexPal.DeriveState(gpu.VRAMMap_TexPal, gpu); - bool textureChanged = GPU::MakeVRAMFlat_TextureCoherent(textureDirty); - bool texPalChanged = GPU::MakeVRAMFlat_TexPalCoherent(texPalDirty); + bool textureChanged = gpu.MakeVRAMFlat_TextureCoherent(textureDirty); + bool texPalChanged = gpu.MakeVRAMFlat_TexPalCoherent(texPalDirty); - FrameIdentical = !(textureChanged || texPalChanged) && RenderFrameIdentical; + FrameIdentical = !(textureChanged || texPalChanged) && gpu.GPU3D.RenderFrameIdentical; if (RenderThreadRunning.load(std::memory_order_relaxed)) { + // "Render thread, you're up! Get moving." Platform::Semaphore_Post(Sema_RenderStart); } else if (!FrameIdentical) { - ClearBuffers(); - RenderPolygons(false, &RenderPolygonRAM[0], RenderNumPolygons); + ClearBuffers(gpu); + RenderPolygons(gpu, false, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons); } } -void SoftRenderer::RestartFrame() +void SoftRenderer::RestartFrame(GPU& gpu) { - SetupRenderThread(); + SetupRenderThread(gpu); + EnableRenderThread(); } -void SoftRenderer::RenderThreadFunc() +void SoftRenderer::RenderThreadFunc(GPU& gpu) { for (;;) { + // Wait for a notice from the main thread to start rendering (or to stop entirely). Platform::Semaphore_Wait(Sema_RenderStart); if (!RenderThreadRunning) return; + // Protect the GPU state from the main thread. + // Some melonDS frontends (though not ours) + // will repeatedly save or load states; + // if they do so while the render thread is busy here, + // the ensuing race conditions may cause a crash + // (since some of the GPU state includes pointers). RenderThreadRendering = true; if (FrameIdentical) - { + { // If no rendering is needed, just say we're done. Platform::Semaphore_Post(Sema_ScanlineCount, 192); } else { - ClearBuffers(); - RenderPolygons(true, &RenderPolygonRAM[0], RenderNumPolygons); + ClearBuffers(gpu); + RenderPolygons(gpu, true, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons); } + // Tell the main thread that we're done rendering + // and that it's safe to access the GPU state again. Platform::Semaphore_Post(Sema_RenderDone); + RenderThreadRendering = false; } } @@ -1756,6 +1799,9 @@ u32* SoftRenderer::GetLine(int line) if (RenderThreadRunning.load(std::memory_order_relaxed)) { if (line < 192) + // We need a scanline, so let's wait for the render thread to finish it. + // (both threads process scanlines from top-to-bottom, + // so we don't need to wait for a specific row) Platform::Semaphore_Wait(Sema_ScanlineCount); } diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 948832f4..9cfdf9ad 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -18,30 +18,34 @@ #pragma once +#include "GPU.h" #include "GPU3D.h" #include "Platform.h" #include #include -namespace GPU3D +namespace melonDS { class SoftRenderer : public Renderer3D { public: - SoftRenderer() noexcept; - virtual ~SoftRenderer() override; - virtual void Reset() override; + SoftRenderer(bool threaded = false) noexcept; + ~SoftRenderer() override; + void Reset(GPU& gpu) override; - virtual void SetRenderSettings(GPU::RenderSettings& settings) override; + void SetThreaded(bool threaded, GPU& gpu) noexcept; + [[nodiscard]] bool IsThreaded() const noexcept { return Threaded; } - virtual void VCount144() override; - virtual void RenderFrame() override; - virtual void RestartFrame() override; - virtual u32* GetLine(int line) override; + void VCount144(GPU& gpu) override; + void RenderFrame(GPU& gpu) override; + void RestartFrame(GPU& gpu) override; + u32* GetLine(int line) override; - void SetupRenderThread(); + void SetupRenderThread(GPU& gpu); + void EnableRenderThread(); void StopRenderThread(); private: + friend void GPU3D::DoSavestate(Savestate* file) noexcept; // Notes on the interpolator: // // This is a theory on how the DS hardware interpolates values. It matches hardware output @@ -64,13 +68,13 @@ private: class Interpolator { public: - Interpolator() {} - Interpolator(s32 x0, s32 x1, s32 w0, s32 w1) + constexpr Interpolator() {} + constexpr Interpolator(s32 x0, s32 x1, s32 w0, s32 w1) { Setup(x0, x1, w0, w1); } - void Setup(s32 x0, s32 x1, s32 w0, s32 w1) + constexpr void Setup(s32 x0, s32 x1, s32 w0, s32 w1) { this->x0 = x0; this->x1 = x1; @@ -122,7 +126,7 @@ private: } } - void SetX(s32 x) + constexpr void SetX(s32 x) { x -= x0; this->x = x; @@ -138,7 +142,7 @@ private: } } - s32 Interpolate(s32 y0, s32 y1) + constexpr s32 Interpolate(s32 y0, s32 y1) const { if (xdiff == 0 || y0 == y1) return y0; @@ -160,7 +164,7 @@ private: } } - s32 InterpolateZ(s32 z0, s32 z1, bool wbuffer) + constexpr s32 InterpolateZ(s32 z0, s32 z1, bool wbuffer) const { if (xdiff == 0 || z0 == z1) return z0; @@ -176,7 +180,7 @@ private: { // Z-buffering: linear interpolation // still doesn't quite match hardware... - s32 base, disp, factor; + s32 base = 0, disp = 0, factor = 0; if (z0 < z1) { @@ -227,12 +231,12 @@ private: class Slope { public: - Slope() {} + constexpr Slope() {} - s32 SetupDummy(s32 x0) + constexpr s32 SetupDummy(s32 x0) { dx = 0; - + this->x0 = x0; this->xmin = x0; this->xmax = x0; @@ -248,7 +252,7 @@ private: return x0; } - s32 Setup(s32 x0, s32 x1, s32 y0, s32 y1, s32 w0, s32 w1, s32 y) + constexpr s32 Setup(s32 x0, s32 x1, s32 y0, s32 y1, s32 w0, s32 w1, s32 y) { this->x0 = x0; this->y = y; @@ -292,7 +296,7 @@ private: XMajor = (Increment > 0x40000); - if (side) + if constexpr (side) { // right @@ -323,7 +327,7 @@ private: return x; } - s32 Step() + constexpr s32 Step() { dx += Increment; y++; @@ -333,9 +337,9 @@ private: return x; } - s32 XVal() + constexpr s32 XVal() const { - s32 ret; + s32 ret = 0; if (Negative) ret = x0 - (dx >> 18); else ret = x0 + (dx >> 18); @@ -343,9 +347,9 @@ private: else if (ret > xmax) ret = xmax; return ret; } - + template - void EdgeParams_XMajor(s32* length, s32* coverage) + constexpr void EdgeParams_XMajor(s32* length, s32* coverage) const { // only do length calc for right side when swapped as it's // only needed for aa calcs, as actual line spans are broken @@ -371,7 +375,7 @@ private: } template - void EdgeParams_YMajor(s32* length, s32* coverage) + constexpr void EdgeParams_YMajor(s32* length, s32* coverage) const { *length = 1; @@ -401,9 +405,9 @@ private: *coverage = cov; } } - + template - void EdgeParams(s32* length, s32* coverage) + constexpr void EdgeParams(s32* length, s32* coverage) const { if (XMajor) return EdgeParams_XMajor(length, coverage); @@ -427,15 +431,16 @@ private: }; template - inline T ReadVRAM_Texture(u32 addr) + inline T ReadVRAM_Texture(u32 addr, const GPU& gpu) const { - return *(T*)&GPU::VRAMFlat_Texture[addr & 0x7FFFF]; + return *(T*)&gpu.VRAMFlat_Texture[addr & 0x7FFFF]; } template - inline T ReadVRAM_TexPal(u32 addr) + inline T ReadVRAM_TexPal(u32 addr, const GPU& gpu) const { - return *(T*)&GPU::VRAMFlat_TexPal[addr & 0x1FFFF]; + return *(T*)&gpu.VRAMFlat_TexPal[addr & 0x1FFFF]; } + u32 AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept; struct RendererPolygon { @@ -450,21 +455,21 @@ private: }; RendererPolygon PolygonList[2048]; - void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha); - u32 RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t); - void PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow); - void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y); - void SetupPolygonRightEdge(RendererPolygon* rp, s32 y); - void SetupPolygon(RendererPolygon* rp, Polygon* polygon); - void RenderShadowMaskScanline(RendererPolygon* rp, s32 y); - void RenderPolygonScanline(RendererPolygon* rp, s32 y); - void RenderScanline(s32 y, int npolys); - u32 CalculateFogDensity(u32 pixeladdr); - void ScanlineFinalPass(s32 y); - void ClearBuffers(); - void RenderPolygons(bool threaded, Polygon** polygons, int npolys); + void TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const; + u32 RenderPixel(const GPU& gpu, const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const; + void PlotTranslucentPixel(const GPU3D& gpu3d, u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow); + void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y) const; + void SetupPolygonRightEdge(RendererPolygon* rp, s32 y) const; + void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const; + void RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon* rp, s32 y); + void RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s32 y); + void RenderScanline(const GPU& gpu, s32 y, int npolys); + u32 CalculateFogDensity(const GPU3D& gpu3d, u32 pixeladdr) const; + void ScanlineFinalPass(const GPU3D& gpu3d, s32 y); + void ClearBuffers(const GPU& gpu); + void RenderPolygons(const GPU& gpu, bool threaded, Polygon** polygons, int npolys); - void RenderThreadFunc(); + void RenderThreadFunc(GPU& gpu); // buffer dimensions are 258x194 to add a offscreen 1px border // which simplifies edge marking tests @@ -503,8 +508,15 @@ private: Platform::Thread* RenderThread; std::atomic_bool RenderThreadRunning; std::atomic_bool RenderThreadRendering; + + // Used by the main thread to tell the render thread to start rendering a frame Platform::Semaphore* Sema_RenderStart; + + // Used by the render thread to tell the main thread that it's done rendering a frame Platform::Semaphore* Sema_RenderDone; + + // Used to allow the main thread to read some scanlines + // before (the 3D portion of) the entire frame is rasterized. Platform::Semaphore* Sema_ScanlineCount; }; -} \ No newline at end of file +} diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index 9d817174..6a6079ed 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -28,12 +28,12 @@ #include "OpenGLSupport.h" #include "GPU_OpenGL_shaders.h" -namespace GPU +namespace melonDS { using namespace OpenGL; -std::unique_ptr GLCompositor::New() noexcept +std::optional GLCompositor::New() noexcept { assert(glBindAttribLocation != nullptr); GLuint CompShader {}; @@ -43,9 +43,9 @@ std::unique_ptr GLCompositor::New() noexcept "CompositorShader", {{"vPosition", 0}, {"vTexcoord", 1}}, {{"oColor", 0}})) - return nullptr; + return std::nullopt; - return std::unique_ptr(new GLCompositor(CompShader)); + return { GLCompositor(CompShader) }; } GLCompositor::GLCompositor(GLuint compShader) noexcept : CompShader(compShader) @@ -87,7 +87,7 @@ GLCompositor::GLCompositor(GLuint compShader) noexcept : CompShader(compShader) glGenBuffers(1, &CompVertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); - glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), CompVertices, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), &CompVertices[0], GL_STATIC_DRAW); glGenVertexArrays(1, &CompVertexArrayID); glBindVertexArray(CompVertexArrayID); @@ -96,7 +96,7 @@ GLCompositor::GLCompositor(GLuint compShader) noexcept : CompShader(compShader) glEnableVertexAttribArray(1); // texcoord glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord))); - glGenFramebuffers(2, CompScreenOutputFB); + glGenFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]); glGenTextures(1, &CompScreenInputTex); glActiveTexture(GL_TEXTURE0); @@ -107,10 +107,10 @@ GLCompositor::GLCompositor(GLuint compShader) noexcept : CompShader(compShader) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL); - glGenTextures(2, CompScreenOutputTex); - for (int i = 0; i < 2; i++) + glGenTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]); + for (GLuint i : CompScreenOutputTex) { - glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]); + glBindTexture(GL_TEXTURE_2D, i); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -124,9 +124,9 @@ GLCompositor::~GLCompositor() { assert(glDeleteFramebuffers != nullptr); - glDeleteFramebuffers(2, CompScreenOutputFB); + glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]); glDeleteTextures(1, &CompScreenInputTex); - glDeleteTextures(2, CompScreenOutputTex); + glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]); glDeleteVertexArrays(1, &CompVertexArrayID); glDeleteBuffers(1, &CompVertexBufferID); @@ -134,14 +134,75 @@ GLCompositor::~GLCompositor() glDeleteProgram(CompShader); } -void GLCompositor::Reset() + +GLCompositor::GLCompositor(GLCompositor&& other) noexcept : + Scale(other.Scale), + ScreenH(other.ScreenH), + ScreenW(other.ScreenW), + CompScaleLoc(other.CompScaleLoc), + Comp3DXPosLoc(other.Comp3DXPosLoc), + CompVertices(other.CompVertices), + CompShader(other.CompShader), + CompVertexBufferID(other.CompVertexBufferID), + CompVertexArrayID(other.CompVertexArrayID), + CompScreenInputTex(other.CompScreenInputTex), + CompScreenOutputTex(other.CompScreenOutputTex), + CompScreenOutputFB(other.CompScreenOutputFB) { + other.CompScreenOutputFB = {}; + other.CompScreenInputTex = {}; + other.CompScreenOutputTex = {}; + other.CompVertexArrayID = {}; + other.CompVertexBufferID = {}; + other.CompShader = {}; +} + +GLCompositor& GLCompositor::operator=(GLCompositor&& other) noexcept +{ + if (this != &other) + { + Scale = other.Scale; + ScreenH = other.ScreenH; + ScreenW = other.ScreenW; + CompScaleLoc = other.CompScaleLoc; + Comp3DXPosLoc = other.Comp3DXPosLoc; + CompVertices = other.CompVertices; + + // Clean up these resources before overwriting them + OpenGL::DeleteShaderProgram(CompShader.data()); + CompShader = other.CompShader; + + glDeleteBuffers(1, &CompVertexBufferID); + CompVertexBufferID = other.CompVertexBufferID; + + glDeleteVertexArrays(1, &CompVertexArrayID); + CompVertexArrayID = other.CompVertexArrayID; + + glDeleteTextures(1, &CompScreenInputTex); + CompScreenInputTex = other.CompScreenInputTex; + + glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]); + CompScreenOutputTex = other.CompScreenOutputTex; + + glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]); + CompScreenOutputFB = other.CompScreenOutputFB; + + other.CompScreenOutputFB = {}; + other.CompScreenInputTex = {}; + other.CompScreenOutputTex = {}; + other.CompVertexArrayID = {}; + other.CompVertexBufferID = {}; + other.CompShader = {}; + } + + return *this; } -void GLCompositor::SetRenderSettings(RenderSettings& settings) +void GLCompositor::SetScaleFactor(int scale) noexcept { - int scale = settings.GL_ScaleFactor; + if (scale == Scale) + return; Scale = scale; ScreenW = 256 * scale; @@ -165,13 +226,12 @@ void GLCompositor::SetRenderSettings(RenderSettings& settings) glBindFramebuffer(GL_FRAMEBUFFER, 0); } -void GLCompositor::Stop() +void GLCompositor::Stop(const GPU& gpu) noexcept { for (int i = 0; i < 2; i++) { - int frontbuf = GPU::FrontBuffer; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[gpu.FrontBuffer]); glClear(GL_COLOR_BUFFER_BIT); } @@ -179,9 +239,9 @@ void GLCompositor::Stop() glBindFramebuffer(GL_FRAMEBUFFER, 0); } -void GLCompositor::RenderFrame() +void GLCompositor::RenderFrame(const GPU& gpu, GLRenderer& renderer) noexcept { - int backbuf = GPU::FrontBuffer ^ 1; + int backbuf = gpu.FrontBuffer ^ 1; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[backbuf]); @@ -199,21 +259,21 @@ void GLCompositor::RenderFrame() glUniform1ui(CompScaleLoc, Scale); // TODO: support setting this midframe, if ever needed - glUniform1i(Comp3DXPosLoc, ((int)GPU3D::RenderXPos << 23) >> 23); + glUniform1i(Comp3DXPosLoc, ((int)gpu.GPU3D.GetRenderXPos() << 23) >> 23); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); - if (GPU::Framebuffer[backbuf][0] && GPU::Framebuffer[backbuf][1]) + if (gpu.Framebuffer[backbuf][0] && gpu.Framebuffer[backbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[backbuf][0]); + GL_UNSIGNED_BYTE, gpu.Framebuffer[backbuf][0].get()); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[backbuf][1]); + GL_UNSIGNED_BYTE, gpu.Framebuffer[backbuf][1].get()); } glActiveTexture(GL_TEXTURE1); - GPU3D::CurrentRenderer->SetupAccelFrame(); + renderer.SetupAccelFrame(); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindVertexArray(CompVertexArrayID); diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index bf4e9d3d..d8472f3c 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,51 +21,51 @@ #include "OpenGLSupport.h" #include -#include +#include -namespace GPU +namespace melonDS { - +class GPU; struct RenderSettings; - +class GLRenderer; class GLCompositor { public: - static std::unique_ptr New() noexcept; + static std::optional New() noexcept; GLCompositor(const GLCompositor&) = delete; GLCompositor& operator=(const GLCompositor&) = delete; + GLCompositor(GLCompositor&&) noexcept; + GLCompositor& operator=(GLCompositor&&) noexcept; ~GLCompositor(); - void Reset(); + void SetScaleFactor(int scale) noexcept; + [[nodiscard]] int GetScaleFactor() const noexcept { return Scale; } - void SetRenderSettings(RenderSettings& settings); - - void Stop(); - void RenderFrame(); + void Stop(const GPU& gpu) noexcept; + void RenderFrame(const GPU& gpu, GLRenderer& renderer) noexcept; void BindOutputTexture(int buf); private: GLCompositor(GLuint CompShader) noexcept; + int Scale = 0; + int ScreenH = 0, ScreenW = 0; - int Scale; - int ScreenH, ScreenW; + GLuint CompShader {}; + GLuint CompScaleLoc = 0; + GLuint Comp3DXPosLoc = 0; - GLuint CompShader; - GLuint CompScaleLoc; - GLuint Comp3DXPosLoc; - - GLuint CompVertexBufferID; - GLuint CompVertexArrayID; + GLuint CompVertexBufferID = 0; + GLuint CompVertexArrayID = 0; struct CompVertex { - float Position[2]; - float Texcoord[2]; + std::array Position {}; + std::array Texcoord {}; }; - CompVertex CompVertices[2 * 3*2]; + std::array CompVertices {}; - GLuint CompScreenInputTex; - GLuint CompScreenOutputTex[2]; - GLuint CompScreenOutputFB[2]; + GLuint CompScreenInputTex = 0; + std::array CompScreenOutputTex {}; + std::array CompScreenOutputFB {}; }; -} \ No newline at end of file +} diff --git a/src/GPU_OpenGL_shaders.h b/src/GPU_OpenGL_shaders.h index b1460cd6..a8c5b951 100644 --- a/src/GPU_OpenGL_shaders.h +++ b/src/GPU_OpenGL_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,8 @@ #ifndef GPU_OPENGL_SHADERS_H #define GPU_OPENGL_SHADERS_H +namespace melonDS +{ const char* kCompositorVS = R"(#version 140 in vec2 vPosition; @@ -866,5 +868,6 @@ void main() +} #endif // GPU_OPENGL_SHADERS_H diff --git a/src/JitBlock.h b/src/JitBlock.h new file mode 100644 index 00000000..9b31d6d7 --- /dev/null +++ b/src/JitBlock.h @@ -0,0 +1,61 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_JITBLOCK_H +#define MELONDS_JITBLOCK_H + +#include "types.h" +#include "TinyVector.h" + +namespace melonDS +{ +typedef void (*JitBlockEntry)(); + +class JitBlock +{ +public: + JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals) + { + Num = num; + NumAddresses = numAddresses; + NumLiterals = numLiterals; + Data.SetLength(numAddresses * 2 + numLiterals); + } + + u32 StartAddr; + u32 StartAddrLocal; + u32 InstrHash, LiteralHash; + u8 Num; + u16 NumAddresses; + u16 NumLiterals; + + JitBlockEntry EntryPoint; + + const u32* AddressRanges() const { return &Data[0]; } + u32* AddressRanges() { return &Data[0]; } + const u32* AddressMasks() const { return &Data[NumAddresses]; } + u32* AddressMasks() { return &Data[NumAddresses]; } + const u32* Literals() const { return &Data[NumAddresses * 2]; } + u32* Literals() { return &Data[NumAddresses * 2]; } + +private: + TinyVector Data; +}; +} + +#endif //MELONDS_JITBLOCK_H diff --git a/src/MemConstants.h b/src/MemConstants.h new file mode 100644 index 00000000..e9aa6b2b --- /dev/null +++ b/src/MemConstants.h @@ -0,0 +1,39 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_MEMCONSTANTS_H +#define MELONDS_MEMCONSTANTS_H + +#include "types.h" + +namespace melonDS +{ +constexpr u32 MainRAMMaxSize = 0x1000000; +constexpr u32 SharedWRAMSize = 0x8000; +constexpr u32 ARM7WRAMSize = 0x10000; +constexpr u32 NWRAMSize = 0x40000; +constexpr u32 ARM9BIOSSize = 0x1000; +constexpr u32 ARM7BIOSSize = 0x4000; +constexpr u32 DSiBIOSSize = 0x10000; +constexpr u32 ITCMPhysicalSize = 0x8000; +constexpr u32 DTCMPhysicalSize = 0x4000; +constexpr u32 ARM7BIOSCRC32 = 0x1280f0d5; +constexpr u32 ARM9BIOSCRC32 = 0x2ab23573; +} + +#endif // MELONDS_MEMCONSTANTS_H \ No newline at end of file diff --git a/src/frontend/qt_sdl/OSD.h b/src/MemRegion.h similarity index 71% rename from src/frontend/qt_sdl/OSD.h rename to src/MemRegion.h index 907496f1..11b3d1da 100644 --- a/src/frontend/qt_sdl/OSD.h +++ b/src/MemRegion.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -16,21 +16,18 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ -#ifndef OSD_H -#define OSD_H +#ifndef MELONDS_MEMREGION_H +#define MELONDS_MEMREGION_H -namespace OSD +#include "types.h" + +// this file exists to break #include cycle loops +namespace melonDS { - -bool Init(bool openGL); -void DeInit(); - -void AddMessage(u32 color, const char* text); - -void Update(); -void DrawNative(QPainter& painter); -void DrawGL(float w, float h); - +struct MemRegion +{ + u8* Mem; + u32 Mask; +}; } - -#endif // OSD_H +#endif //MELONDS_MEMREGION_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 81fb8dcd..94a24029 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -16,6 +16,7 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include #include #include #include @@ -33,23 +34,23 @@ #include "AREngine.h" #include "Platform.h" #include "FreeBIOS.h" -#include "GPU3D.h" - -#ifdef JIT_ENABLED -#include "ARMJIT.h" -#include "ARMJIT_Memory.h" -#endif +#include "Args.h" +#include "version.h" #include "DSi.h" #include "DSi_SPI_TSC.h" #include "DSi_NWifi.h" #include "DSi_Camera.h" #include "DSi_DSP.h" +#include "ARMJIT.h" +#include "ARMJIT_Memory.h" +namespace melonDS +{ using namespace Platform; -namespace NDS -{ +const s32 kMaxIterationCycles = 64; +const s32 kIterationCycleMargin = 8; // timing notes // @@ -73,186 +74,69 @@ namespace NDS // // timings for GBA slot and wifi are set up at runtime -int ConsoleType; +NDS* NDS::Current = nullptr; -u8 ARM9MemTimings[0x40000][8]; -u32 ARM9Regions[0x40000]; -u8 ARM7MemTimings[0x20000][4]; -u32 ARM7Regions[0x20000]; - -ARMv5* ARM9; -ARMv4* ARM7; - -#ifdef JIT_ENABLED -bool EnableJIT; -#endif - -u32 NumFrames; -u32 NumLagFrames; -bool LagFrameFlag; -u64 LastSysClockCycles; -u64 FrameStartTimestamp; - -int CurCPU; - -const s32 kMaxIterationCycles = 64; -const s32 kIterationCycleMargin = 8; - -u32 ARM9ClockShift; - -// no need to worry about those overflowing, they can keep going for atleast 4350 years -u64 ARM9Timestamp, ARM9Target; -u64 ARM7Timestamp, ARM7Target; -u64 SysTimestamp; - -SchedEvent SchedList[Event_MAX]; -u32 SchedListMask; - -u32 CPUStop; - -u8 ARM9BIOS[0x1000]; -u8 ARM7BIOS[0x4000]; - -u8* MainRAM; -u32 MainRAMMask; - -u8* SharedWRAM; -u8 WRAMCnt; - -// putting them together so they're always next to each other -MemRegion SWRAM_ARM9; -MemRegion SWRAM_ARM7; - -u8* ARM7WRAM; - -u16 ExMemCnt[2]; - -// TODO: these belong in NDSCart! -u8 ROMSeed0[2*8]; -u8 ROMSeed1[2*8]; - -// IO shit -u32 IME[2]; -u32 IE[2], IF[2]; -u32 IE2, IF2; - -u8 PostFlag9; -u8 PostFlag7; -u16 PowerControl9; -u16 PowerControl7; - -u16 WifiWaitCnt; - -u16 ARM7BIOSProt; - -Timer Timers[8]; -u8 TimerCheckMask[2]; -u64 TimerTimestamp[2]; - -DMA* DMAs[8]; -u32 DMA9Fill[4]; - -u16 IPCSync9, IPCSync7; -u16 IPCFIFOCnt9, IPCFIFOCnt7; -FIFO IPCFIFO9; // FIFO in which the ARM9 writes -FIFO IPCFIFO7; - -u16 DivCnt; -u32 DivNumerator[2]; -u32 DivDenominator[2]; -u32 DivQuotient[2]; -u32 DivRemainder[2]; - -u16 SqrtCnt; -u32 SqrtVal[2]; -u32 SqrtRes; - -u32 KeyInput; -u16 KeyCnt; -u16 RCnt; - -bool Running; - -bool RunningGame; - -void DivDone(u32 param); -void SqrtDone(u32 param); -void RunTimer(u32 tid, s32 cycles); -void UpdateWifiTimings(); -void SetWifiWaitCnt(u16 val); -void SetGBASlotTimings(); - - -bool Init() +NDS::NDS() noexcept : + NDS( + NDSArgs { + nullptr, + nullptr, + std::make_unique(bios_arm9_bin), + std::make_unique(bios_arm7_bin), + Firmware(0), + } + ) { - ARM9 = new ARMv5(); - ARM7 = new ARMv4(); - -#ifdef JIT_ENABLED - ARMJIT::Init(); -#else - MainRAM = new u8[0x1000000]; - ARM7WRAM = new u8[ARM7WRAMSize]; - SharedWRAM = new u8[SharedWRAMSize]; -#endif - - DMAs[0] = new DMA(0, 0); - DMAs[1] = new DMA(0, 1); - DMAs[2] = new DMA(0, 2); - DMAs[3] = new DMA(0, 3); - DMAs[4] = new DMA(1, 0); - DMAs[5] = new DMA(1, 1); - DMAs[6] = new DMA(1, 2); - DMAs[7] = new DMA(1, 3); - - if (!NDSCart::Init()) return false; - if (!GBACart::Init()) return false; - if (!GPU::Init()) return false; - if (!SPU::Init()) return false; - if (!SPI::Init()) return false; - if (!RTC::Init()) return false; - if (!Wifi::Init()) return false; - - if (!DSi::Init()) return false; - - if (!AREngine::Init()) return false; - - return true; } -void DeInit() -{ +NDS::NDS(NDSArgs&& args, int type) noexcept : + ConsoleType(type), + ARM7BIOS(*args.ARM7BIOS), + ARM9BIOS(*args.ARM9BIOS), + ARM7BIOSNative(CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32), + ARM9BIOSNative(CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32), + JIT(*this, args.JIT), + SPU(*this, args.BitDepth, args.Interpolation), + GPU(*this, std::move(args.Renderer3D)), + SPI(*this, std::move(args.Firmware)), + RTC(*this), + Wifi(*this), + NDSCartSlot(*this, std::move(args.NDSROM)), + GBACartSlot(type == 1 ? nullptr : std::move(args.GBAROM)), + AREngine(*this), + ARM9(*this, args.GDB, args.JIT.has_value()), + ARM7(*this, args.GDB, args.JIT.has_value()), #ifdef JIT_ENABLED - ARMJIT::DeInit(); + EnableJIT(args.JIT.has_value()), #endif - - delete ARM9; - ARM9 = nullptr; - - delete ARM7; - ARM7 = nullptr; - - for (int i = 0; i < 8; i++) - { - delete DMAs[i]; - DMAs[i] = nullptr; + DMAs { + DMA(0, 0, *this), + DMA(0, 1, *this), + DMA(0, 2, *this), + DMA(0, 3, *this), + DMA(1, 0, *this), + DMA(1, 1, *this), + DMA(1, 2, *this), + DMA(1, 3, *this), } +{ + RegisterEventFunc(Event_Div, 0, MemberEventFunc(NDS, DivDone)); + RegisterEventFunc(Event_Sqrt, 0, MemberEventFunc(NDS, SqrtDone)); - NDSCart::DeInit(); - GBACart::DeInit(); - GPU::DeInit(); - SPU::DeInit(); - SPI::DeInit(); - RTC::DeInit(); - Wifi::DeInit(); + MainRAM = JIT.Memory.GetMainRAM(); + SharedWRAM = JIT.Memory.GetSharedWRAM(); + ARM7WRAM = JIT.Memory.GetARM7WRAM(); +} - DSi::DeInit(); - - AREngine::DeInit(); +NDS::~NDS() noexcept +{ + UnregisterEventFunc(Event_Div, 0); + UnregisterEventFunc(Event_Sqrt, 0); + // The destructor for each component is automatically called by the compiler } -void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) +void NDS::SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) { addrstart >>= 2; addrend >>= 2; @@ -291,10 +175,10 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, ARM9Regions[i] = region; } - ARM9->UpdateRegionTimings(addrstart<<2, addrend<<2); + ARM9.UpdateRegionTimings(addrstart<<2, addrend<<2); } -void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) +void NDS::SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) { addrstart >>= 3; addrend >>= 3; @@ -325,7 +209,23 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, } } -void InitTimings() +#ifdef JIT_ENABLED +void NDS::SetJITArgs(std::optional args) noexcept +{ + if (args) + { // If we want to turn the JIT on... + JIT.SetJITArgs(*args); + } + else if (args.has_value() != EnableJIT) + { // Else if we want to turn the JIT off, and it wasn't already off... + JIT.ResetBlockCache(); + } + + EnableJIT = args.has_value(); +} +#endif + +void NDS::InitTimings() { // TODO, eventually: // VRAM is initially unmapped. The timings should be those of void regions. @@ -362,7 +262,7 @@ void InitTimings() // handled later: GBA slot, wifi } -bool NeedsDirectBoot() +bool NDS::NeedsDirectBoot() const { if (ConsoleType == 1) { @@ -371,165 +271,164 @@ bool NeedsDirectBoot() } else { - // internal BIOS does not support direct boot - if (!Platform::GetConfigBool(Platform::ExternalBIOSEnable)) + // DSi/3DS firmwares aren't bootable, neither is the generated firmware + if (!SPI.GetFirmware().IsBootable()) return true; - // DSi/3DS firmwares aren't bootable - if (!SPI_Firmware::GetFirmware()->IsBootable()) + // FreeBIOS requires direct boot (it can't boot firmware) + if (!IsLoadedARM9BIOSKnownNative() || !IsLoadedARM7BIOSKnownNative()) return true; return false; } } -void SetupDirectBoot(const std::string& romname) +void NDS::SetupDirectBoot() { - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); + u32 cartid = NDSCartSlot.GetCart()->ID(); + const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); + MapSharedWRAM(3); - if (ConsoleType == 1) + // Copy the Nintendo logo from the NDS ROM header to the ARM9 BIOS if using FreeBIOS + // Games need this for DS<->GBA comm to work + if (!IsLoadedARM9BIOSKnownNative()) { - DSi::SetupDirectBoot(); - } - else - { - u32 cartid = NDSCart::Cart->ID(); - const u8* cartrom = NDSCart::Cart->GetROM(); - MapSharedWRAM(3); - - // setup main RAM data - - for (u32 i = 0; i < 0x170; i+=4) - { - u32 tmp = *(u32*)&cartrom[i]; - ARM9Write32(0x027FFE00+i, tmp); - } - - ARM9Write32(0x027FF800, cartid); - ARM9Write32(0x027FF804, cartid); - ARM9Write16(0x027FF808, header.HeaderCRC16); - ARM9Write16(0x027FF80A, header.SecureAreaCRC16); - - ARM9Write16(0x027FF850, 0x5835); - - ARM9Write32(0x027FFC00, cartid); - ARM9Write32(0x027FFC04, cartid); - ARM9Write16(0x027FFC08, header.HeaderCRC16); - ARM9Write16(0x027FFC0A, header.SecureAreaCRC16); - - ARM9Write16(0x027FFC10, 0x5835); - ARM9Write16(0x027FFC30, 0xFFFF); - ARM9Write16(0x027FFC40, 0x0001); - - u32 arm9start = 0; - - // load the ARM9 secure area - if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) - { - u8 securearea[0x800]; - NDSCart::DecryptSecureArea(securearea); - - for (u32 i = 0; i < 0x800; i+=4) - { - ARM9Write32(header.ARM9RAMAddress+i, *(u32*)&securearea[i]); - arm9start += 4; - } - } - - // CHECKME: firmware seems to load this in 0x200 byte chunks - - for (u32 i = arm9start; i < header.ARM9Size; i+=4) - { - u32 tmp = *(u32*)&cartrom[header.ARM9ROMOffset+i]; - ARM9Write32(header.ARM9RAMAddress+i, tmp); - } - - for (u32 i = 0; i < header.ARM7Size; i+=4) - { - u32 tmp = *(u32*)&cartrom[header.ARM7ROMOffset+i]; - ARM7Write32(header.ARM7RAMAddress+i, tmp); - } - - ARM7BIOSProt = 0x1204; - - SPI_Firmware::SetupDirectBoot(false); - - ARM9->CP15Write(0x100, 0x00012078); - ARM9->CP15Write(0x200, 0x00000042); - ARM9->CP15Write(0x201, 0x00000042); - ARM9->CP15Write(0x300, 0x00000002); - ARM9->CP15Write(0x502, 0x15111011); - ARM9->CP15Write(0x503, 0x05100011); - ARM9->CP15Write(0x600, 0x04000033); - ARM9->CP15Write(0x601, 0x04000033); - ARM9->CP15Write(0x610, 0x0200002B); - ARM9->CP15Write(0x611, 0x0200002B); - ARM9->CP15Write(0x620, 0x00000000); - ARM9->CP15Write(0x621, 0x00000000); - ARM9->CP15Write(0x630, 0x08000035); - ARM9->CP15Write(0x631, 0x08000035); - ARM9->CP15Write(0x640, 0x0300001B); - ARM9->CP15Write(0x641, 0x0300001B); - ARM9->CP15Write(0x650, 0x00000000); - ARM9->CP15Write(0x651, 0x00000000); - ARM9->CP15Write(0x660, 0xFFFF001D); - ARM9->CP15Write(0x661, 0xFFFF001D); - ARM9->CP15Write(0x670, 0x027FF017); - ARM9->CP15Write(0x671, 0x027FF017); - ARM9->CP15Write(0x910, 0x0300000A); - ARM9->CP15Write(0x911, 0x00000020); + memcpy(ARM9BIOS.data() + 0x20, header.NintendoLogo, 0x9C); } - NDSCart::SetupDirectBoot(romname); + // setup main RAM data - ARM9->R[12] = header.ARM9EntryAddress; - ARM9->R[13] = 0x03002F7C; - ARM9->R[14] = header.ARM9EntryAddress; - ARM9->R_IRQ[0] = 0x03003F80; - ARM9->R_SVC[0] = 0x03003FC0; + for (u32 i = 0; i < 0x170; i+=4) + { + u32 tmp = *(u32*)&cartrom[i]; + NDS::ARM9Write32(0x027FFE00+i, tmp); + } - ARM7->R[12] = header.ARM7EntryAddress; - ARM7->R[13] = 0x0380FD80; - ARM7->R[14] = header.ARM7EntryAddress; - ARM7->R_IRQ[0] = 0x0380FF80; - ARM7->R_SVC[0] = 0x0380FFC0; + NDS::ARM9Write32(0x027FF800, cartid); + NDS::ARM9Write32(0x027FF804, cartid); + NDS::ARM9Write16(0x027FF808, header.HeaderCRC16); + NDS::ARM9Write16(0x027FF80A, header.SecureAreaCRC16); - ARM9->JumpTo(header.ARM9EntryAddress); - ARM7->JumpTo(header.ARM7EntryAddress); + NDS::ARM9Write16(0x027FF850, 0x5835); + + NDS::ARM9Write32(0x027FFC00, cartid); + NDS::ARM9Write32(0x027FFC04, cartid); + NDS::ARM9Write16(0x027FFC08, header.HeaderCRC16); + NDS::ARM9Write16(0x027FFC0A, header.SecureAreaCRC16); + + NDS::ARM9Write16(0x027FFC10, 0x5835); + NDS::ARM9Write16(0x027FFC30, 0xFFFF); + NDS::ARM9Write16(0x027FFC40, 0x0001); + + u32 arm9start = 0; + + // load the ARM9 secure area + if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) + { + u8 securearea[0x800]; + NDSCartSlot.DecryptSecureArea(securearea); + + for (u32 i = 0; i < 0x800; i+=4) + { + NDS::ARM9Write32(header.ARM9RAMAddress+i, *(u32*)&securearea[i]); + arm9start += 4; + } + } + + // CHECKME: firmware seems to load this in 0x200 byte chunks + + for (u32 i = arm9start; i < header.ARM9Size; i+=4) + { + u32 tmp = *(u32*)&cartrom[header.ARM9ROMOffset+i]; + NDS::ARM9Write32(header.ARM9RAMAddress+i, tmp); + } + + for (u32 i = 0; i < header.ARM7Size; i+=4) + { + u32 tmp = *(u32*)&cartrom[header.ARM7ROMOffset+i]; + NDS::ARM7Write32(header.ARM7RAMAddress+i, tmp); + } + + ARM7BIOSProt = 0x1204; + + SPI.GetFirmwareMem()->SetupDirectBoot(); + + ARM9.CP15Write(0x100, 0x00012078); + ARM9.CP15Write(0x200, 0x00000042); + ARM9.CP15Write(0x201, 0x00000042); + ARM9.CP15Write(0x300, 0x00000002); + ARM9.CP15Write(0x502, 0x15111011); + ARM9.CP15Write(0x503, 0x05100011); + ARM9.CP15Write(0x600, 0x04000033); + ARM9.CP15Write(0x601, 0x04000033); + ARM9.CP15Write(0x610, 0x0200002B); + ARM9.CP15Write(0x611, 0x0200002B); + ARM9.CP15Write(0x620, 0x00000000); + ARM9.CP15Write(0x621, 0x00000000); + ARM9.CP15Write(0x630, 0x08000035); + ARM9.CP15Write(0x631, 0x08000035); + ARM9.CP15Write(0x640, 0x0300001B); + ARM9.CP15Write(0x641, 0x0300001B); + ARM9.CP15Write(0x650, 0x00000000); + ARM9.CP15Write(0x651, 0x00000000); + ARM9.CP15Write(0x660, 0xFFFF001D); + ARM9.CP15Write(0x661, 0xFFFF001D); + ARM9.CP15Write(0x670, 0x027FF017); + ARM9.CP15Write(0x671, 0x027FF017); + ARM9.CP15Write(0x910, 0x0300000A); + ARM9.CP15Write(0x911, 0x00000020); +} + +void NDS::SetupDirectBoot(const std::string& romname) +{ + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); + SetupDirectBoot(); + + NDSCartSlot.SetupDirectBoot(romname); + + ARM9.R[12] = header.ARM9EntryAddress; + ARM9.R[13] = 0x03002F7C; + ARM9.R[14] = header.ARM9EntryAddress; + ARM9.R_IRQ[0] = 0x03003F80; + ARM9.R_SVC[0] = 0x03003FC0; + + ARM7.R[12] = header.ARM7EntryAddress; + ARM7.R[13] = 0x0380FD80; + ARM7.R[14] = header.ARM7EntryAddress; + ARM7.R_IRQ[0] = 0x0380FF80; + ARM7.R_SVC[0] = 0x0380FFC0; + + ARM9.JumpTo(header.ARM9EntryAddress); + ARM7.JumpTo(header.ARM7EntryAddress); PostFlag9 = 0x01; PostFlag7 = 0x01; PowerControl9 = 0x820F; - GPU::SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); // checkme RCnt = 0x8000; - NDSCart::SPICnt = 0x8000; + NDSCartSlot.SetSPICnt(0x8000); - SPU::SetBias(0x200); + SPU.SetBias(0x200); SetWifiWaitCnt(0x0030); } -void Reset() +void NDS::Reset() { Platform::FileHandle* f; u32 i; -#ifdef JIT_ENABLED - EnableJIT = Platform::GetConfigBool(Platform::JIT_Enable); -#endif - RunningGame = false; LastSysClockCycles = 0; // BIOS files are now loaded by the frontend -#ifdef JIT_ENABLED - ARMJIT::Reset(); -#endif + JIT.Reset(); if (ConsoleType == 1) { @@ -546,7 +445,7 @@ void Reset() // has to be called before InitTimings // otherwise some PU settings are completely // unitialised on the first run - ARM9->CP15Reset(); + ARM9.CP15Reset(); ARM9Timestamp = 0; ARM9Target = 0; ARM7Timestamp = 0; ARM7Target = 0; @@ -595,8 +494,8 @@ void Reset() DivCnt = 0; SqrtCnt = 0; - ARM9->Reset(); - ARM7->Reset(); + ARM9.Reset(); + ARM7.Reset(); CPUStop = 0; @@ -606,50 +505,34 @@ void Reset() TimerTimestamp[0] = 0; TimerTimestamp[1] = 0; - for (i = 0; i < 8; i++) DMAs[i]->Reset(); + for (i = 0; i < 8; i++) DMAs[i].Reset(); memset(DMA9Fill, 0, 4*4); - memset(SchedList, 0, sizeof(SchedList)); + for (i = 0; i < Event_MAX; i++) + { + SchedEvent& evt = SchedList[i]; + + evt.Timestamp = 0; + evt.FuncID = 0; + evt.Param = 0; + } SchedListMask = 0; KeyInput = 0x007F03FF; - KeyCnt = 0; + KeyCnt[0] = 0; + KeyCnt[1] = 0; RCnt = 0; - NDSCart::Reset(); - GBACart::Reset(); - GPU::Reset(); - SPU::Reset(); - SPI::Reset(); - RTC::Reset(); - Wifi::Reset(); - - // TODO: move the SOUNDBIAS/degrade logic to SPU? - - // The SOUNDBIAS register does nothing on DSi - SPU::SetApplyBias(ConsoleType == 0); - - bool degradeAudio = true; - - if (ConsoleType == 1) - { - DSi::Reset(); - KeyInput &= ~(1 << (16+6)); - degradeAudio = false; - } - - int bitDepth = Platform::GetConfigInt(Platform::AudioBitDepth); - if (bitDepth == 1) // Always 10-bit - degradeAudio = true; - else if (bitDepth == 2) // Always 16-bit - degradeAudio = false; - - SPU::SetDegrade10Bit(degradeAudio); - - AREngine::Reset(); + GPU.Reset(); + NDSCartSlot.Reset(); + GBACartSlot.Reset(); + SPU.Reset(); + SPI.Reset(); + RTC.Reset(); + Wifi.Reset(); } -void Start() +void NDS::Start() { Running = true; } @@ -671,7 +554,7 @@ static const char* StopReasonName(Platform::StopReason reason) } } -void Stop(Platform::StopReason reason) +void NDS::Stop(Platform::StopReason reason) { Platform::LogLevel level; switch (reason) @@ -692,113 +575,11 @@ void Stop(Platform::StopReason reason) Log(level, "Stopping emulated console (Reason: %s)\n", StopReasonName(reason)); Running = false; Platform::SignalStop(reason); - GPU::Stop(); - SPU::Stop(); - - if (ConsoleType == 1) - DSi::Stop(); + GPU.Stop(); + SPU.Stop(); } -bool DoSavestate_Scheduler(Savestate* file) -{ - // this is a bit of a hack - // but uh, your local coder realized that the scheduler list contains function pointers - // and that storing those as-is is not a very good idea - // unless you want it to crash and burn - - // this is the solution your local coder came up with. - // it's gross but I think it's the best solution for this problem. - // just remember to add here if you add more event callbacks, kay? - // atleast until we come up with something more elegant. - - void (*eventfuncs[])(u32) = - { - GPU::StartScanline, GPU::StartHBlank, GPU::FinishFrame, - SPU::Mix, - Wifi::USTimer, - - GPU::DisplayFIFO, - NDSCart::ROMPrepareData, NDSCart::ROMEndTransfer, - NDSCart::SPITransferDone, - SPI::TransferDone, - DivDone, - SqrtDone, - - DSi_SDHost::FinishRX, - DSi_SDHost::FinishTX, - DSi_NWifi::MSTimer, - DSi_CamModule::IRQ, - DSi_CamModule::TransferScanline, - DSi_DSP::DSPCatchUpU32, - - nullptr - }; - - int len = Event_MAX; - if (file->Saving) - { - for (int i = 0; i < len; i++) - { - SchedEvent* evt = &SchedList[i]; - - u32 funcid = 0xFFFFFFFF; - if (evt->Func) - { - for (int j = 0; eventfuncs[j]; j++) - { - if (evt->Func == eventfuncs[j]) - { - funcid = j; - break; - } - } - if (funcid == 0xFFFFFFFF) - { - Log(LogLevel::Error, "savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK ARISOTURA.\n", i); - return false; - } - } - - file->Var32(&funcid); - file->Var64(&evt->Timestamp); - file->Var32(&evt->Param); - } - } - else - { - for (int i = 0; i < len; i++) - { - SchedEvent* evt = &SchedList[i]; - - u32 funcid; - file->Var32(&funcid); - - if (funcid != 0xFFFFFFFF) - { - for (int j = 0; ; j++) - { - if (!eventfuncs[j]) - { - Log(LogLevel::Error, "savestate: VERY BAD!!!!!! EVENT FUNCTION POINTER ID %d IS OUT OF RANGE. HAX?????\n", j); - return false; - } - if (j == funcid) break; - } - - evt->Func = eventfuncs[funcid]; - } - else - evt->Func = nullptr; - - file->Var64(&evt->Timestamp); - file->Var32(&evt->Param); - } - } - - return true; -} - -bool DoSavestate(Savestate* file) +bool NDS::DoSavestate(Savestate* file) { file->Section("NDSG"); @@ -870,10 +651,13 @@ bool DoSavestate(Savestate* file) file->VarArray(DMA9Fill, 4*sizeof(u32)); - if (!DoSavestate_Scheduler(file)) + for (int i = 0; i < Event_MAX; i++) { - Platform::Log(Platform::LogLevel::Error, "savestate: failed to %s scheduler state\n", file->Saving ? "save" : "load"); - return false; + SchedEvent& evt = SchedList[i]; + + file->Var64(&evt.Timestamp); + file->Var32(&evt.FuncID); + file->Var32(&evt.Param); } file->Var32(&SchedListMask); file->Var64(&ARM9Timestamp); @@ -888,7 +672,7 @@ bool DoSavestate(Savestate* file) file->Bool32(&LagFrameFlag); // TODO: save KeyInput???? - file->Var16(&KeyCnt); + file->VarArray(KeyCnt, 2*sizeof(u16)); file->Var16(&RCnt); file->Var8(&WRAMCnt); @@ -908,113 +692,85 @@ bool DoSavestate(Savestate* file) } for (int i = 0; i < 8; i++) - DMAs[i]->DoSavestate(file); + DMAs[i].DoSavestate(file); - ARM9->DoSavestate(file); - ARM7->DoSavestate(file); + ARM9.DoSavestate(file); + ARM7.DoSavestate(file); - NDSCart::DoSavestate(file); + NDSCartSlot.DoSavestate(file); if (ConsoleType == 0) - GBACart::DoSavestate(file); - GPU::DoSavestate(file); - SPU::DoSavestate(file); - SPI::DoSavestate(file); - RTC::DoSavestate(file); - Wifi::DoSavestate(file); + GBACartSlot.DoSavestate(file); + GPU.DoSavestate(file); + SPU.DoSavestate(file); + SPI.DoSavestate(file); + RTC.DoSavestate(file); + Wifi.DoSavestate(file); - if (ConsoleType == 1) - DSi::DoSavestate(file); + DoSavestateExtra(file); // Handles DSi state if applicable if (!file->Saving) { - GPU::SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); - SPU::SetPowerCnt(PowerControl7 & 0x0001); - Wifi::SetPowerCnt(PowerControl7 & 0x0002); - } + SPU.SetPowerCnt(PowerControl7 & 0x0001); + Wifi.SetPowerCnt(PowerControl7 & 0x0002); #ifdef JIT_ENABLED - if (!file->Saving) - { - ARMJIT::ResetBlockCache(); - ARMJIT_Memory::Reset(); - } + JIT.Reset(); #endif + } file->Finish(); return true; } -void SetConsoleType(int type) +void NDS::SetNDSCart(std::unique_ptr&& cart) { - ConsoleType = type; + NDSCartSlot.SetCart(std::move(cart)); + // The existing cart will always be ejected; + // if cart is null, then that's equivalent to ejecting a cart + // without inserting a new one. } -bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) -{ - if (!NDSCart::LoadROM(romdata, romlen)) - return false; - - if (savedata && savelen) - NDSCart::LoadSave(savedata, savelen); - - return true; -} - -void LoadSave(const u8* savedata, u32 savelen) +void NDS::SetNDSSave(const u8* savedata, u32 savelen) { if (savedata && savelen) - NDSCart::LoadSave(savedata, savelen); + NDSCartSlot.SetSaveMemory(savedata, savelen); } -void EjectCart() +void NDS::SetGBASave(const u8* savedata, u32 savelen) { - NDSCart::EjectCart(); + if (ConsoleType == 0 && savedata && savelen) + { + GBACartSlot.SetSaveMemory(savedata, savelen); + } + } -bool CartInserted() +void NDS::LoadGBAAddon(int type) { - return NDSCart::Cart != nullptr; + GBACartSlot.LoadAddon(type); } -bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) -{ - if (!GBACart::LoadROM(romdata, romlen)) - return false; - - if (savedata && savelen) - GBACart::LoadSave(savedata, savelen); - - return true; -} - -void LoadGBAAddon(int type) -{ - GBACart::LoadAddon(type); -} - -void EjectGBACart() -{ - GBACart::EjectCart(); -} - -void LoadBIOS() +void NDS::LoadBIOS() { Reset(); } -bool IsLoadedARM9BIOSBuiltIn() +void NDS::SetARM7BIOS(const std::array& bios) noexcept { - return memcmp(NDS::ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0; + ARM7BIOS = bios; + ARM7BIOSNative = CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32; } -bool IsLoadedARM7BIOSBuiltIn() +void NDS::SetARM9BIOS(const std::array& bios) noexcept { - return memcmp(NDS::ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0; + ARM9BIOS = bios; + ARM9BIOSNative = CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32; } -u64 NextTarget() +u64 NDS::NextTarget() { u64 minEvent = UINT64_MAX; @@ -1039,7 +795,7 @@ u64 NextTarget() return max; } -void RunSystem(u64 timestamp) +void NDS::RunSystem(u64 timestamp) { SysTimestamp = timestamp; @@ -1049,10 +805,14 @@ void RunSystem(u64 timestamp) if (!mask) break; if (mask & 0x1) { - if (SchedList[i].Timestamp <= SysTimestamp) + SchedEvent& evt = SchedList[i]; + + if (evt.Timestamp <= SysTimestamp) { SchedListMask &= ~(1< -u32 RunFrame() +u64 NDS::NextTargetSleep() +{ + u64 minEvent = UINT64_MAX; + + u32 mask = SchedListMask; + for (int i = 0; i < Event_MAX; i++) + { + if (!mask) break; + if (i == Event_SPU || i == Event_RTC) + { + if (mask & 0x1) + { + if (SchedList[i].Timestamp < minEvent) + minEvent = SchedList[i].Timestamp; + } + } + + mask >>= 1; + } + + return minEvent; +} + +void NDS::RunSystemSleep(u64 timestamp) +{ + u64 offset = timestamp - SysTimestamp; + SysTimestamp = timestamp; + + u32 mask = SchedListMask; + for (int i = 0; i < Event_MAX; i++) + { + if (!mask) break; + if (i == Event_SPU || i == Event_RTC) + { + if (mask & 0x1) + { + SchedEvent& evt = SchedList[i]; + + if (evt.Timestamp <= SysTimestamp) + { + SchedListMask &= ~(1<>= 1; + } +} + +template +u32 NDS::RunFrame() { FrameStartTimestamp = SysTimestamp; + GPU.TotalScanlines = 0; + LagFrameFlag = true; - bool runFrame = Running && !(CPUStop & 0x40000000); - if (runFrame) + bool runFrame = Running && !(CPUStop & CPUStop_Sleep); + while (Running) { - GPU::StartFrame(); + u64 frametarget = SysTimestamp + 560190; - while (Running && GPU::TotalScanlines==0) + if (CPUStop & CPUStop_Sleep) { - u64 target = NextTarget(); - ARM9Target = target << ARM9ClockShift; - CurCPU = 0; + // we are running in sleep mode + // we still need to run the RTC during this mode + // we also keep outputting audio, so that frontends using audio sync don't skyrocket to 1000+FPS - if (CPUStop & 0x80000000) + while (Running && (SysTimestamp < frametarget)) { - // GXFIFO stall - s32 cycles = GPU3D::CyclesToRunFor(); + u64 target = NextTargetSleep(); + if (target > frametarget) + target = frametarget; - ARM9Timestamp = std::min(ARM9Target, ARM9Timestamp+(cycles<Run(); - if (!(CPUStop & 0x80000000)) DMAs[1]->Run(); - if (!(CPUStop & 0x80000000)) DMAs[2]->Run(); - if (!(CPUStop & 0x80000000)) DMAs[3]->Run(); - if (ConsoleType == 1) DSi::RunNDMAs(0); - } - else - { -#ifdef JIT_ENABLED - if (EnableJIT) - ARM9->ExecuteJIT(); - else -#endif - ARM9->Execute(); + ARM9Timestamp = target << ARM9ClockShift; + ARM7Timestamp = target; + TimerTimestamp[0] = target; + TimerTimestamp[1] = target; + GPU.GPU3D.Timestamp = target; + RunSystemSleep(target); + + if (!(CPUStop & CPUStop_Sleep)) + break; } - RunTimers(0); - GPU3D::Run(); + if (SysTimestamp >= frametarget) + GPU.BlankFrame(); + } + else + { + ARM9.CheckGdbIncoming(); + ARM7.CheckGdbIncoming(); - target = ARM9Timestamp >> ARM9ClockShift; - CurCPU = 1; - - while (ARM7Timestamp < target) + if (!(CPUStop & CPUStop_Wakeup)) { - ARM7Target = target; // might be changed by a reschedule + GPU.StartFrame(); + } + CPUStop &= ~CPUStop_Wakeup; - if (CPUStop & 0x0FFF0000) + while (Running && GPU.TotalScanlines==0) + { + u64 target = NextTarget(); + ARM9Target = target << ARM9ClockShift; + CurCPU = 0; + + if (CPUStop & CPUStop_GXStall) { - DMAs[4]->Run(); - DMAs[5]->Run(); - DMAs[6]->Run(); - DMAs[7]->Run(); - if (ConsoleType == 1) DSi::RunNDMAs(1); + // GXFIFO stall + s32 cycles = GPU.GPU3D.CyclesToRunFor(); + + ARM9Timestamp = std::min(ARM9Target, ARM9Timestamp+(cycles<(*this); + dsi.RunNDMAs(0); + } } else { #ifdef JIT_ENABLED if (EnableJIT) - ARM7->ExecuteJIT(); + ARM9.ExecuteJIT(); else #endif - ARM7->Execute(); + ARM9.Execute(); } - RunTimers(1); - } + RunTimers(0); + GPU.GPU3D.Run(); - RunSystem(target); + target = ARM9Timestamp >> ARM9ClockShift; + CurCPU = 1; - if (CPUStop & 0x40000000) - { - // checkme: when is sleep mode effective? - CancelEvent(Event_LCD); - GPU::TotalScanlines = 263; - break; + while (ARM7Timestamp < target) + { + ARM7Target = target; // might be changed by a reschedule + + if (CPUStop & CPUStop_DMA7) + { + DMAs[4].Run(); + DMAs[5].Run(); + DMAs[6].Run(); + DMAs[7].Run(); + if (ConsoleType == 1) + { + auto& dsi = dynamic_cast(*this); + dsi.RunNDMAs(1); + } + } + else + { +#ifdef JIT_ENABLED + if (EnableJIT) + ARM7.ExecuteJIT(); + else +#endif + ARM7.Execute(); + } + + RunTimers(1); + } + + RunSystem(target); + + if (CPUStop & CPUStop_Sleep) + { + break; + } } } + if (GPU.TotalScanlines == 0) + continue; + #ifdef DEBUG_CHECK_DESYNC Log(LogLevel::Debug, "[%08X%08X] ARM9=%ld, ARM7=%ld, GPU=%ld\n", (u32)(SysTimestamp>>32), (u32)SysTimestamp, (ARM9Timestamp>>1)-SysTimestamp, ARM7Timestamp-SysTimestamp, - GPU3D::Timestamp-SysTimestamp); + GPU.GPU3D.Timestamp-SysTimestamp); #endif - SPU::TransferOutput(); + SPU.TransferOutput(); + break; } // In the context of TASes, frame count is traditionally the primary measure of emulated time, @@ -1160,27 +1034,23 @@ u32 RunFrame() if (LagFrameFlag) NumLagFrames++; - if (runFrame) - return GPU::TotalScanlines; + if (Running) + return GPU.TotalScanlines; else return 263; } -u32 RunFrame() +u32 NDS::RunFrame() { #ifdef JIT_ENABLED if (EnableJIT) - return NDS::ConsoleType == 1 - ? RunFrame() - : RunFrame(); + return RunFrame(); else #endif - return NDS::ConsoleType == 1 - ? RunFrame() - : RunFrame(); + return RunFrame(); } -void Reschedule(u64 target) +void NDS::Reschedule(u64 target) { if (CurCPU == 0) { @@ -1194,7 +1064,21 @@ void Reschedule(u64 target) } } -void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param) +void NDS::RegisterEventFunc(u32 id, u32 funcid, EventFunc func) +{ + SchedEvent& evt = SchedList[id]; + + evt.Funcs[funcid] = func; +} + +void NDS::UnregisterEventFunc(u32 id, u32 funcid) +{ + SchedEvent& evt = SchedList[id]; + + evt.Funcs.erase(funcid); +} + +void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param) { if (SchedListMask & (1<Timestamp += delay; + evt.Timestamp += delay; else { if (CurCPU == 0) - evt->Timestamp = (ARM9Timestamp >> ARM9ClockShift) + delay; + evt.Timestamp = (ARM9Timestamp >> ARM9ClockShift) + delay; else - evt->Timestamp = ARM7Timestamp + delay; + evt.Timestamp = ARM7Timestamp + delay; } - evt->Func = func; - evt->Param = param; + evt.FuncID = funcid; + evt.Param = param; SchedListMask |= (1<Timestamp); + Reschedule(evt.Timestamp); } -void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param) -{ - if (SchedListMask & (1<Timestamp = timestamp; - evt->Func = func; - evt->Param = param; - - SchedListMask |= (1<Timestamp); -} - -void CancelEvent(u32 id) +void NDS::CancelEvent(u32 id) { SchedListMask &= ~(1<SetTouchCoords(x, y); } -void ReleaseScreen() +void NDS::ReleaseScreen() { - if (ConsoleType == 1) - { - DSi_SPI_TSC::SetTouchCoords(0x000, 0xFFF); - } - else - { - SPI_TSC::SetTouchCoords(0x000, 0xFFF); - KeyInput |= (1 << (16+6)); - } + SPI.GetTSC()->SetTouchCoords(0x000, 0xFFF); } -void SetKeyMask(u32 mask) +void NDS::CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey) +{ + u16 cnt = KeyCnt[cpu]; + if (!(cnt & (1<<14))) // IRQ disabled + return; + + u32 mask = (cnt & 0x03FF); + oldkey &= mask; + newkey &= mask; + + bool oldmatch, newmatch; + if (cnt & (1<<15)) + { + // logical AND + + oldmatch = (oldkey == 0); + newmatch = (newkey == 0); + } + else + { + // logical OR + + oldmatch = (oldkey != mask); + newmatch = (newkey != mask); + } + + if ((!oldmatch) && newmatch) + SetIRQ(cpu, IRQ_Keypad); +} + +void NDS::SetKeyMask(u32 mask) { u32 key_lo = mask & 0x3FF; u32 key_hi = (mask >> 10) & 0x3; + u32 oldkey = KeyInput; KeyInput &= 0xFFFCFC00; KeyInput |= key_lo | (key_hi << 16); + + CheckKeyIRQ(0, oldkey, KeyInput); + CheckKeyIRQ(1, oldkey, KeyInput); } -bool IsLidClosed() +bool NDS::IsLidClosed() const { if (KeyInput & (1<<23)) return true; return false; } -void SetLidClosed(bool closed) +void NDS::SetLidClosed(bool closed) { if (closed) { @@ -1299,29 +1182,12 @@ void SetLidClosed(bool closed) { KeyInput &= ~(1<<23); SetIRQ(1, IRQ_LidOpen); - CPUStop &= ~0x40000000; - GPU3D::RestartFrame(); } } -void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) +void NDS::MicInputFrame(s16* data, int samples) { - // TODO: support things like the GBA-slot camera addon - // whenever these are emulated - - if (ConsoleType == 1) - { - switch (cam) - { - case 0: return DSi_CamModule::Camera0->InputFrame(data, width, height, rgb); - case 1: return DSi_CamModule::Camera1->InputFrame(data, width, height, rgb); - } - } -} - -void MicInputFrame(s16* data, int samples) -{ - return SPI_TSC::MicInputFrame(data, samples); + return SPI.GetTSC()->MicInputFrame(data, samples); } /*int ImportSRAM(u8* data, u32 length) @@ -1330,21 +1196,19 @@ void MicInputFrame(s16* data, int samples) }*/ -void Halt() +void NDS::Halt() { Log(LogLevel::Info, "Halt()\n"); Running = false; } -void MapSharedWRAM(u8 val) +void NDS::MapSharedWRAM(u8 val) { if (val == WRAMCnt) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapSWRAM(); -#endif + JIT.Memory.RemapSWRAM(); WRAMCnt = val; @@ -1381,7 +1245,7 @@ void MapSharedWRAM(u8 val) } -void UpdateWifiTimings() +void NDS::UpdateWifiTimings() { if (PowerControl7 & 0x0002) { @@ -1398,7 +1262,7 @@ void UpdateWifiTimings() } } -void SetWifiWaitCnt(u16 val) +void NDS::SetWifiWaitCnt(u16 val) { if (WifiWaitCnt == val) return; @@ -1406,7 +1270,7 @@ void SetWifiWaitCnt(u16 val) UpdateWifiTimings(); } -void SetGBASlotTimings() +void NDS::SetGBASlotTimings() { const int ntimings[4] = {10, 8, 6, 18}; const u16 openbus[4] = {0xFE08, 0x0000, 0x0000, 0xFFFF}; @@ -1441,51 +1305,61 @@ void SetGBASlotTimings() // for example, the Cartridge Construction Kit relies on this to determine that // the GBA slot is empty - GBACart::SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]); + GBACartSlot.SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]); } -void UpdateIRQ(u32 cpu) +void NDS::UpdateIRQ(u32 cpu) { - ARM* arm = cpu ? (ARM*)ARM7 : (ARM*)ARM9; + ARM& arm = cpu ? (ARM&)ARM7 : (ARM&)ARM9; if (IME[cpu] & 0x1) { - arm->IRQ = !!(IE[cpu] & IF[cpu]); + arm.IRQ = !!(IE[cpu] & IF[cpu]); if ((ConsoleType == 1) && cpu) - arm->IRQ |= !!(IE2 & IF2); + arm.IRQ |= !!(IE2 & IF2); } else { - arm->IRQ = 0; + arm.IRQ = 0; } } -void SetIRQ(u32 cpu, u32 irq) +void NDS::SetIRQ(u32 cpu, u32 irq) { IF[cpu] |= (1 << irq); UpdateIRQ(cpu); + + if ((cpu == 1) && (CPUStop & CPUStop_Sleep)) + { + if (IE[1] & (1 << irq)) + { + CPUStop &= ~CPUStop_Sleep; + CPUStop |= CPUStop_Wakeup; + GPU.GPU3D.RestartFrame(GPU); + } + } } -void ClearIRQ(u32 cpu, u32 irq) +void NDS::ClearIRQ(u32 cpu, u32 irq) { IF[cpu] &= ~(1 << irq); UpdateIRQ(cpu); } -void SetIRQ2(u32 irq) +void NDS::SetIRQ2(u32 irq) { IF2 |= (1 << irq); UpdateIRQ(1); } -void ClearIRQ2(u32 irq) +void NDS::ClearIRQ2(u32 irq) { IF2 &= ~(1 << irq); UpdateIRQ(1); } -bool HaltInterrupted(u32 cpu) +bool NDS::HaltInterrupted(u32 cpu) const { if (cpu == 0) { @@ -1502,62 +1376,66 @@ bool HaltInterrupted(u32 cpu) return false; } -void StopCPU(u32 cpu, u32 mask) +void NDS::StopCPU(u32 cpu, u32 mask) { if (cpu) { CPUStop |= (mask << 16); - ARM7->Halt(2); + ARM7.Halt(2); } else { CPUStop |= mask; - ARM9->Halt(2); + ARM9.Halt(2); } } -void ResumeCPU(u32 cpu, u32 mask) +void NDS::ResumeCPU(u32 cpu, u32 mask) { if (cpu) mask <<= 16; CPUStop &= ~mask; } -void GXFIFOStall() +void NDS::GXFIFOStall() { - if (CPUStop & 0x80000000) return; + if (CPUStop & CPUStop_GXStall) return; - CPUStop |= 0x80000000; + CPUStop |= CPUStop_GXStall; - if (CurCPU == 1) ARM9->Halt(2); + if (CurCPU == 1) ARM9.Halt(2); else { - DMAs[0]->StallIfRunning(); - DMAs[1]->StallIfRunning(); - DMAs[2]->StallIfRunning(); - DMAs[3]->StallIfRunning(); - if (ConsoleType == 1) DSi::StallNDMAs(); + DMAs[0].StallIfRunning(); + DMAs[1].StallIfRunning(); + DMAs[2].StallIfRunning(); + DMAs[3].StallIfRunning(); + if (ConsoleType == 1) + { + auto& dsi = dynamic_cast(*this); + dsi.StallNDMAs(); + } } } -void GXFIFOUnstall() +void NDS::GXFIFOUnstall() { - CPUStop &= ~0x80000000; + CPUStop &= ~CPUStop_GXStall; } -void EnterSleepMode() +void NDS::EnterSleepMode() { - if (CPUStop & 0x40000000) return; + if (CPUStop & CPUStop_Sleep) return; - CPUStop |= 0x40000000; - ARM7->Halt(2); + CPUStop |= CPUStop_Sleep; + ARM7.Halt(2); } -u32 GetPC(u32 cpu) +u32 NDS::GetPC(u32 cpu) const { - return cpu ? ARM7->R[15] : ARM9->R[15]; + return cpu ? ARM7.R[15] : ARM9.R[15]; } -u64 GetSysClockCycles(int num) +u64 NDS::GetSysClockCycles(int num) { u64 ret; @@ -1584,19 +1462,19 @@ u64 GetSysClockCycles(int num) return ret; } -void NocashPrint(u32 ncpu, u32 addr) +void NDS::NocashPrint(u32 ncpu, u32 addr) { // addr: debug string - ARM* cpu = ncpu ? (ARM*)ARM7 : (ARM*)ARM9; - u8 (*readfn)(u32) = ncpu ? NDS::ARM7Read8 : NDS::ARM9Read8; + ARM* cpu = ncpu ? (ARM*)&ARM7 : (ARM*)&ARM9; + u8 (NDS::*readfn)(u32) = ncpu ? &NDS::ARM7Read8 : &NDS::ARM9Read8; char output[1024]; int ptr = 0; for (int i = 0; i < 120 && ptr < 1023; ) { - char ch = readfn(addr++); + char ch = (this->*readfn)(addr++); i++; if (ch == '%') @@ -1604,7 +1482,7 @@ void NocashPrint(u32 ncpu, u32 addr) char cmd[16]; int j; for (j = 0; j < 15; ) { - char ch2 = readfn(addr++); + char ch2 = (this->*readfn)(addr++); i++; if (i >= 120) break; if (ch2 == '%') break; @@ -1616,40 +1494,40 @@ void NocashPrint(u32 ncpu, u32 addr) if (cmd[0] == 'r') { - if (!strcmp(cmd, "r0")) sprintf(subs, "%08X", cpu->R[0]); - else if (!strcmp(cmd, "r1")) sprintf(subs, "%08X", cpu->R[1]); - else if (!strcmp(cmd, "r2")) sprintf(subs, "%08X", cpu->R[2]); - else if (!strcmp(cmd, "r3")) sprintf(subs, "%08X", cpu->R[3]); - else if (!strcmp(cmd, "r4")) sprintf(subs, "%08X", cpu->R[4]); - else if (!strcmp(cmd, "r5")) sprintf(subs, "%08X", cpu->R[5]); - else if (!strcmp(cmd, "r6")) sprintf(subs, "%08X", cpu->R[6]); - else if (!strcmp(cmd, "r7")) sprintf(subs, "%08X", cpu->R[7]); - else if (!strcmp(cmd, "r8")) sprintf(subs, "%08X", cpu->R[8]); - else if (!strcmp(cmd, "r9")) sprintf(subs, "%08X", cpu->R[9]); - else if (!strcmp(cmd, "r10")) sprintf(subs, "%08X", cpu->R[10]); - else if (!strcmp(cmd, "r11")) sprintf(subs, "%08X", cpu->R[11]); - else if (!strcmp(cmd, "r12")) sprintf(subs, "%08X", cpu->R[12]); - else if (!strcmp(cmd, "r13")) sprintf(subs, "%08X", cpu->R[13]); - else if (!strcmp(cmd, "r14")) sprintf(subs, "%08X", cpu->R[14]); - else if (!strcmp(cmd, "r15")) sprintf(subs, "%08X", cpu->R[15]); + if (!strcmp(cmd, "r0")) snprintf(subs, sizeof(subs), "%08X", cpu->R[0]); + else if (!strcmp(cmd, "r1")) snprintf(subs, sizeof(subs), "%08X", cpu->R[1]); + else if (!strcmp(cmd, "r2")) snprintf(subs, sizeof(subs), "%08X", cpu->R[2]); + else if (!strcmp(cmd, "r3")) snprintf(subs, sizeof(subs), "%08X", cpu->R[3]); + else if (!strcmp(cmd, "r4")) snprintf(subs, sizeof(subs), "%08X", cpu->R[4]); + else if (!strcmp(cmd, "r5")) snprintf(subs, sizeof(subs), "%08X", cpu->R[5]); + else if (!strcmp(cmd, "r6")) snprintf(subs, sizeof(subs), "%08X", cpu->R[6]); + else if (!strcmp(cmd, "r7")) snprintf(subs, sizeof(subs), "%08X", cpu->R[7]); + else if (!strcmp(cmd, "r8")) snprintf(subs, sizeof(subs), "%08X", cpu->R[8]); + else if (!strcmp(cmd, "r9")) snprintf(subs, sizeof(subs), "%08X", cpu->R[9]); + else if (!strcmp(cmd, "r10")) snprintf(subs, sizeof(subs), "%08X", cpu->R[10]); + else if (!strcmp(cmd, "r11")) snprintf(subs, sizeof(subs), "%08X", cpu->R[11]); + else if (!strcmp(cmd, "r12")) snprintf(subs, sizeof(subs), "%08X", cpu->R[12]); + else if (!strcmp(cmd, "r13")) snprintf(subs, sizeof(subs), "%08X", cpu->R[13]); + else if (!strcmp(cmd, "r14")) snprintf(subs, sizeof(subs), "%08X", cpu->R[14]); + else if (!strcmp(cmd, "r15")) snprintf(subs, sizeof(subs), "%08X", cpu->R[15]); } else { - if (!strcmp(cmd, "sp")) sprintf(subs, "%08X", cpu->R[13]); - else if (!strcmp(cmd, "lr")) sprintf(subs, "%08X", cpu->R[14]); - else if (!strcmp(cmd, "pc")) sprintf(subs, "%08X", cpu->R[15]); - else if (!strcmp(cmd, "frame")) sprintf(subs, "%u", NumFrames); - else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU::VCount); - else if (!strcmp(cmd, "totalclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(0)); - else if (!strcmp(cmd, "lastclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(1)); + if (!strcmp(cmd, "sp")) snprintf(subs, sizeof(subs), "%08X", cpu->R[13]); + else if (!strcmp(cmd, "lr")) snprintf(subs, sizeof(subs), "%08X", cpu->R[14]); + else if (!strcmp(cmd, "pc")) snprintf(subs, sizeof(subs), "%08X", cpu->R[15]); + else if (!strcmp(cmd, "frame")) snprintf(subs, sizeof(subs), "%u", NumFrames); + else if (!strcmp(cmd, "scanline")) snprintf(subs, sizeof(subs), "%u", GPU.VCount); + else if (!strcmp(cmd, "totalclks")) snprintf(subs, sizeof(subs), "%" PRIu64, GetSysClockCycles(0)); + else if (!strcmp(cmd, "lastclks")) snprintf(subs, sizeof(subs), "%" PRIu64, GetSysClockCycles(1)); else if (!strcmp(cmd, "zeroclks")) { - sprintf(subs, "%s", ""); + snprintf(subs, sizeof(subs), "%s", ""); GetSysClockCycles(1); } } - int slen = strlen(subs); + int slen = strnlen(subs, sizeof(subs)); if ((ptr+slen) > 1023) slen = 1023-ptr; strncpy(&output[ptr], subs, slen); ptr += slen; @@ -1665,16 +1543,14 @@ void NocashPrint(u32 ncpu, u32 addr) Log(LogLevel::Debug, "%s", output); } - - -void MonitorARM9Jump(u32 addr) +void NDS::MonitorARM9Jump(u32 addr) { // checkme: can the entrypoint addr be THUMB? // also TODO: make it work in DSi mode - if ((!RunningGame) && NDSCart::Cart) + if ((!RunningGame) && NDSCartSlot.GetCart()) { - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); if (addr == header.ARM9EntryAddress) { Log(LogLevel::Info, "Game is now booting\n"); @@ -1685,7 +1561,7 @@ void MonitorARM9Jump(u32 addr) -void HandleTimerOverflow(u32 tid) +void NDS::HandleTimerOverflow(u32 tid) { Timer* timer = &Timers[tid]; @@ -1718,7 +1594,7 @@ void HandleTimerOverflow(u32 tid) } } -void RunTimer(u32 tid, s32 cycles) +void NDS::RunTimer(u32 tid, s32 cycles) { Timer* timer = &Timers[tid]; @@ -1730,7 +1606,7 @@ void RunTimer(u32 tid, s32 cycles) } } -void RunTimers(u32 cpu) +void NDS::RunTimers(u32 cpu) { u32 timermask = TimerCheckMask[cpu]; s32 cycles; @@ -1750,7 +1626,7 @@ void RunTimers(u32 cpu) const s32 TimerPrescaler[4] = {0, 6, 8, 10}; -u16 TimerGetCounter(u32 timer) +u16 NDS::TimerGetCounter(u32 timer) { RunTimers(timer>>2); u32 ret = Timers[timer].Counter; @@ -1758,7 +1634,7 @@ u16 TimerGetCounter(u32 timer) return ret >> 10; } -void TimerStart(u32 id, u16 cnt) +void NDS::TimerStart(u32 id, u16 cnt) { Timer* timer = &Timers[id]; u16 curstart = timer->Cnt & (1<<7); @@ -1782,93 +1658,49 @@ void TimerStart(u32 id, u16 cnt) -// matching NDMA modes for DSi -const u32 NDMAModes[] = -{ - // ARM9 - - 0x10, // immediate - 0x06, // VBlank - 0x07, // HBlank - 0x08, // scanline start - 0x09, // mainmem FIFO - 0x04, // DS cart slot - 0xFF, // GBA cart slot - 0x0A, // GX FIFO - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - - // ARM7 - - 0x30, // immediate - 0x26, // VBlank - 0x24, // DS cart slot - 0xFF, // wifi / GBA cart slot (TODO) -}; - -bool DMAsInMode(u32 cpu, u32 mode) +bool NDS::DMAsInMode(u32 cpu, u32 mode) const { cpu <<= 2; - if (DMAs[cpu+0]->IsInMode(mode)) return true; - if (DMAs[cpu+1]->IsInMode(mode)) return true; - if (DMAs[cpu+2]->IsInMode(mode)) return true; - if (DMAs[cpu+3]->IsInMode(mode)) return true; - - if (ConsoleType == 1) - { - cpu >>= 2; - return DSi::NDMAsInMode(cpu, NDMAModes[mode]); - } + if (DMAs[cpu+0].IsInMode(mode)) return true; + if (DMAs[cpu+1].IsInMode(mode)) return true; + if (DMAs[cpu+2].IsInMode(mode)) return true; + if (DMAs[cpu+3].IsInMode(mode)) return true; return false; } -bool DMAsRunning(u32 cpu) +bool NDS::DMAsRunning(u32 cpu) const { cpu <<= 2; - if (DMAs[cpu+0]->IsRunning()) return true; - if (DMAs[cpu+1]->IsRunning()) return true; - if (DMAs[cpu+2]->IsRunning()) return true; - if (DMAs[cpu+3]->IsRunning()) return true; - if (ConsoleType == 1) - { - if (DSi::NDMAsRunning(cpu>>2)) return true; - } + if (DMAs[cpu+0].IsRunning()) return true; + if (DMAs[cpu+1].IsRunning()) return true; + if (DMAs[cpu+2].IsRunning()) return true; + if (DMAs[cpu+3].IsRunning()) return true; + return false; } -void CheckDMAs(u32 cpu, u32 mode) +void NDS::CheckDMAs(u32 cpu, u32 mode) { cpu <<= 2; - DMAs[cpu+0]->StartIfNeeded(mode); - DMAs[cpu+1]->StartIfNeeded(mode); - DMAs[cpu+2]->StartIfNeeded(mode); - DMAs[cpu+3]->StartIfNeeded(mode); - - if (ConsoleType == 1) - { - cpu >>= 2; - DSi::CheckNDMAs(cpu, NDMAModes[mode]); - } + DMAs[cpu+0].StartIfNeeded(mode); + DMAs[cpu+1].StartIfNeeded(mode); + DMAs[cpu+2].StartIfNeeded(mode); + DMAs[cpu+3].StartIfNeeded(mode); } -void StopDMAs(u32 cpu, u32 mode) +void NDS::StopDMAs(u32 cpu, u32 mode) { cpu <<= 2; - DMAs[cpu+0]->StopIfNeeded(mode); - DMAs[cpu+1]->StopIfNeeded(mode); - DMAs[cpu+2]->StopIfNeeded(mode); - DMAs[cpu+3]->StopIfNeeded(mode); - - if (ConsoleType == 1) - { - cpu >>= 2; - DSi::StopNDMAs(cpu, NDMAModes[mode]); - } + DMAs[cpu+0].StopIfNeeded(mode); + DMAs[cpu+1].StopIfNeeded(mode); + DMAs[cpu+2].StopIfNeeded(mode); + DMAs[cpu+3].StopIfNeeded(mode); } -void DivDone(u32 param) +void NDS::DivDone(u32 param) { DivCnt &= ~0xC000; @@ -1946,15 +1778,15 @@ void DivDone(u32 param) DivCnt |= 0x4000; } -void StartDiv() +void NDS::StartDiv() { - NDS::CancelEvent(NDS::Event_Div); + CancelEvent(Event_Div); DivCnt |= 0x8000; - NDS::ScheduleEvent(NDS::Event_Div, false, ((DivCnt&0x3)==0) ? 18:34, DivDone, 0); + ScheduleEvent(Event_Div, false, ((DivCnt&0x3)==0) ? 18:34, 0, 0); } // http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2 -void SqrtDone(u32 param) +void NDS::SqrtDone(u32 param) { u64 val; u32 res = 0; @@ -1994,45 +1826,44 @@ void SqrtDone(u32 param) SqrtRes = res; } -void StartSqrt() +void NDS::StartSqrt() { - NDS::CancelEvent(NDS::Event_Sqrt); + CancelEvent(Event_Sqrt); SqrtCnt |= 0x8000; - NDS::ScheduleEvent(NDS::Event_Sqrt, false, 13, SqrtDone, 0); + ScheduleEvent(Event_Sqrt, false, 13, 0, 0); } -void debug(u32 param) +void NDS::debug(u32 param) { - Log(LogLevel::Debug, "ARM9 PC=%08X LR=%08X %08X\n", ARM9->R[15], ARM9->R[14], ARM9->R_IRQ[1]); - Log(LogLevel::Debug, "ARM7 PC=%08X LR=%08X %08X\n", ARM7->R[15], ARM7->R[14], ARM7->R_IRQ[1]); + Log(LogLevel::Debug, "ARM9 PC=%08X LR=%08X %08X\n", ARM9.R[15], ARM9.R[14], ARM9.R_IRQ[1]); + Log(LogLevel::Debug, "ARM7 PC=%08X LR=%08X %08X\n", ARM7.R[15], ARM7.R[14], ARM7.R_IRQ[1]); Log(LogLevel::Debug, "ARM9 IME=%08X IE=%08X IF=%08X\n", IME[0], IE[0], IF[0]); Log(LogLevel::Debug, "ARM7 IME=%08X IE=%08X IF=%08X IE2=%04X IF2=%04X\n", IME[1], IE[1], IF[1], IE2, IF2); //for (int i = 0; i < 9; i++) - // printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]); + // printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]); - FILE* - shit = fopen("debug/crayon.bin", "wb"); - fwrite(ARM9->ITCM, 0x8000, 1, shit); + Platform::FileHandle* shit = Platform::OpenFile("debug/pokeplat.bin", FileMode::Write); + Platform::FileWrite(ARM9.ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { - u32 val = ARM7Read32(i); - fwrite(&val, 4, 1, shit); + u32 val = NDS::ARM7Read32(i); + Platform::FileWrite(&val, 4, 1, shit); } for (u32 i = 0x037F0000; i < 0x03810000; i+=4) { - u32 val = ARM7Read32(i); - fwrite(&val, 4, 1, shit); + u32 val = NDS::ARM7Read32(i); + Platform::FileWrite(&val, 4, 1, shit); } for (u32 i = 0x06000000; i < 0x06040000; i+=4) { - u32 val = ARM7Read32(i); - fwrite(&val, 4, 1, shit); + u32 val = NDS::ARM7Read32(i); + Platform::FileWrite(&val, 4, 1, shit); } - fclose(shit); + Platform::CloseFile(shit); /*FILE* shit = fopen("debug/directboot9.bin", "wb"); @@ -2053,7 +1884,7 @@ void debug(u32 param) -u8 ARM9Read8(u32 addr) +u8 NDS::ARM9Read8(u32 addr) { if ((addr & 0xFFFFF000) == 0xFFFF0000) { @@ -2076,42 +1907,43 @@ u8 ARM9Read8(u32 addr) } case 0x04000000: - return ARM9IORead8(addr); + // Specifically want to call the NDS version, not a subclass + return NDS::ARM9IORead8(addr); case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadPalette(addr); + return GPU.ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return GPU.ReadVRAM_ABG(addr); + case 0x00200000: return GPU.ReadVRAM_BBG(addr); + case 0x00400000: return GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU.ReadVRAM_BOBJ(addr); + default: return GPU.ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadOAM(addr); + return GPU.ReadOAM(addr); case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - if (addr & 0x1) return GBACart::ROMRead(addr-1) >> 8; - return GBACart::ROMRead(addr) & 0xFF; + if (addr & 0x1) return GBACartSlot.ROMRead(addr-1) >> 8; + return GBACartSlot.ROMRead(addr) & 0xFF; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr); + return GBACartSlot.SRAMRead(addr); } Log(LogLevel::Debug, "unknown arm9 read8 %08X\n", addr); return 0; } -u16 ARM9Read16(u32 addr) +u16 NDS::ARM9Read16(u32 addr) { addr &= ~0x1; @@ -2136,42 +1968,42 @@ u16 ARM9Read16(u32 addr) } case 0x04000000: - return ARM9IORead16(addr); + return NDS::ARM9IORead16(addr); case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadPalette(addr); + return GPU.ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return GPU.ReadVRAM_ABG(addr); + case 0x00200000: return GPU.ReadVRAM_BBG(addr); + case 0x00400000: return GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU.ReadVRAM_BOBJ(addr); + default: return GPU.ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadOAM(addr); + return GPU.ReadOAM(addr); case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr); + return GBACartSlot.ROMRead(addr); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8); } - //if (addr) Log(LogLevel::Warn, "unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]); + //if (addr) Log(LogLevel::Warn, "unknown arm9 read16 %08X %08X\n", addr, ARM9.R[15]); return 0; } -u32 ARM9Read32(u32 addr) +u32 NDS::ARM9Read32(u32 addr) { addr &= ~0x3; @@ -2196,67 +2028,63 @@ u32 ARM9Read32(u32 addr) } case 0x04000000: - return ARM9IORead32(addr); + return NDS::ARM9IORead32(addr); case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadPalette(addr); + return GPU.ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return GPU.ReadVRAM_ABG(addr); + case 0x00200000: return GPU.ReadVRAM_BBG(addr); + case 0x00400000: return GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU.ReadVRAM_BOBJ(addr); + default: return GPU.ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadOAM(addr & 0x7FF); + return GPU.ReadOAM(addr & 0x7FF); case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr) | - (GBACart::ROMRead(addr+2) << 16); + return GBACartSlot.ROMRead(addr) | + (GBACartSlot.ROMRead(addr+2) << 16); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8) | - (GBACart::SRAMRead(addr+2) << 16) | - (GBACart::SRAMRead(addr+3) << 24); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8) | + (GBACartSlot.SRAMRead(addr+2) << 16) | + (GBACartSlot.SRAMRead(addr+3) << 24); } - //Log(LogLevel::Warn, "unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]); + //Log(LogLevel::Warn, "unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9.R[15], ARM9.R[12]); return 0; } -void ARM9Write8(u32 addr, u8 val) +void NDS::ARM9Write8(u32 addr, u8 val) { switch (addr & 0xFF000000) { case 0x02000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM9.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u8*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; case 0x04000000: - ARM9IOWrite8(addr, val); + NDS::ARM9IOWrite8(addr, val); return; case 0x05000000: @@ -2270,149 +2098,137 @@ void ARM9Write8(u32 addr, u8 val) case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val); + GBACartSlot.SRAMWrite(addr, val); return; } Log(LogLevel::Debug, "unknown arm9 write8 %08X %02X\n", addr, val); } -void ARM9Write16(u32 addr, u16 val) +void NDS::ARM9Write16(u32 addr, u16 val) { addr &= ~0x1; switch (addr & 0xFF000000) { case 0x02000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM9.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u16*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; case 0x04000000: - ARM9IOWrite16(addr, val); + NDS::ARM9IOWrite16(addr, val); return; case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WritePalette(addr, val); + GPU.WritePalette(addr, val); return; case 0x06000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU.WriteVRAM_BOBJ(addr, val); return; + default: GPU.WriteVRAM_LCDC(addr, val); return; } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WriteOAM(addr, val); + GPU.WriteOAM(addr, val); return; case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val); + GBACartSlot.ROMWrite(addr, val); return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, val >> 8); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, val >> 8); return; } //if (addr) Log(LogLevel::Warn, "unknown arm9 write16 %08X %04X\n", addr, val); } -void ARM9Write32(u32 addr, u32 val) +void NDS::ARM9Write32(u32 addr, u32 val) { addr &= ~0x3; switch (addr & 0xFF000000) { case 0x02000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return ; case 0x03000000: if (SWRAM_ARM9.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u32*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; case 0x04000000: - ARM9IOWrite32(addr, val); + NDS::ARM9IOWrite32(addr, val); return; case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WritePalette(addr, val); + GPU.WritePalette(addr, val); return; case 0x06000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU.WriteVRAM_BOBJ(addr, val); return; + default: GPU.WriteVRAM_LCDC(addr, val); return; } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WriteOAM(addr, val); + GPU.WriteOAM(addr, val); return; case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val & 0xFFFF); - GBACart::ROMWrite(addr+2, val >> 16); + GBACartSlot.ROMWrite(addr, val & 0xFFFF); + GBACartSlot.ROMWrite(addr+2, val >> 16); return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, (val >> 8) & 0xFF); - GBACart::SRAMWrite(addr+2, (val >> 16) & 0xFF); - GBACart::SRAMWrite(addr+3, val >> 24); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACartSlot.SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACartSlot.SRAMWrite(addr+3, val >> 24); return; } - //Log(LogLevel::Warn, "unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]); + //Log(LogLevel::Warn, "unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9.R[15]); } -bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) +bool NDS::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) { switch (addr & 0xFF000000) { @@ -2433,7 +2249,7 @@ bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write) { - region->Mem = ARM9BIOS; + region->Mem = &ARM9BIOS[0]; region->Mask = 0xFFF; return true; } @@ -2444,14 +2260,14 @@ bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) -u8 ARM7Read8(u32 addr) +u8 NDS::ARM7Read8(u32 addr) { if (addr < 0x00004000) { // TODO: check the boundary? is it 4000 or higher on regular DS? - if (ARM7->R[15] >= 0x00004000) + if (ARM7.R[15] >= 0x00004000) return 0xFF; - if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFF; return *(u8*)&ARM7BIOS[addr]; @@ -2477,48 +2293,48 @@ u8 ARM7Read8(u32 addr) return *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)]; case 0x04000000: - return ARM7IORead8(addr); + return NDS::ARM7IORead8(addr); case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - if (addr & 0x1) return Wifi::Read(addr-1) >> 8; - return Wifi::Read(addr) & 0xFF; + if (addr & 0x1) return Wifi.Read(addr-1) >> 8; + return Wifi.Read(addr) & 0xFF; } break; case 0x06000000: case 0x06800000: - return GPU::ReadVRAM_ARM7(addr); + return GPU.ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - if (addr & 0x1) return GBACart::ROMRead(addr-1) >> 8; - return GBACart::ROMRead(addr) & 0xFF; + if (addr & 0x1) return GBACartSlot.ROMRead(addr-1) >> 8; + return GBACartSlot.ROMRead(addr) & 0xFF; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr); + return GBACartSlot.SRAMRead(addr); } - Log(LogLevel::Debug, "unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]); + Log(LogLevel::Debug, "unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7.R[15], ARM7.R[0], ARM7.R[1]); return 0; } -u16 ARM7Read16(u32 addr) +u16 NDS::ARM7Read16(u32 addr) { addr &= ~0x1; if (addr < 0x00004000) { - if (ARM7->R[15] >= 0x00004000) + if (ARM7.R[15] >= 0x00004000) return 0xFFFF; - if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFF; return *(u16*)&ARM7BIOS[addr]; @@ -2544,47 +2360,47 @@ u16 ARM7Read16(u32 addr) return *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)]; case 0x04000000: - return ARM7IORead16(addr); + return NDS::ARM7IORead16(addr); case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - return Wifi::Read(addr); + return Wifi.Read(addr); } break; case 0x06000000: case 0x06800000: - return GPU::ReadVRAM_ARM7(addr); + return GPU.ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr); + return GBACartSlot.ROMRead(addr); case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8); } - Log(LogLevel::Debug, "unknown arm7 read16 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 read16 %08X %08X\n", addr, ARM7.R[15]); return 0; } -u32 ARM7Read32(u32 addr) +u32 NDS::ARM7Read32(u32 addr) { addr &= ~0x3; if (addr < 0x00004000) { - if (ARM7->R[15] >= 0x00004000) + if (ARM7.R[15] >= 0x00004000) return 0xFFFFFFFF; - if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFFFFFF; return *(u32*)&ARM7BIOS[addr]; @@ -2610,88 +2426,78 @@ u32 ARM7Read32(u32 addr) return *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)]; case 0x04000000: - return ARM7IORead32(addr); + return NDS::ARM7IORead32(addr); case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16); + return Wifi.Read(addr) | (Wifi.Read(addr+2) << 16); } break; case 0x06000000: case 0x06800000: - return GPU::ReadVRAM_ARM7(addr); + return GPU.ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr) | - (GBACart::ROMRead(addr+2) << 16); + return GBACartSlot.ROMRead(addr) | + (GBACartSlot.ROMRead(addr+2) << 16); case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8) | - (GBACart::SRAMRead(addr+2) << 16) | - (GBACart::SRAMRead(addr+3) << 24); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8) | + (GBACartSlot.SRAMRead(addr+2) << 16) | + (GBACartSlot.SRAMRead(addr+3) << 24); } - //Log(LogLevel::Warn, "unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]); + //Log(LogLevel::Warn, "unknown arm7 read32 %08X | %08X\n", addr, ARM7.R[15]); return 0; } -void ARM7Write8(u32 addr, u8 val) +void NDS::ARM7Write8(u32 addr, u8 val) { switch (addr & 0xFF800000) { case 0x02000000: case 0x02800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u8*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; case 0x04000000: - ARM7IOWrite8(addr, val); + NDS::ARM7IOWrite8(addr, val); return; case 0x06000000: case 0x06800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); -#endif - GPU::WriteVRAM_ARM7(addr, val); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); + GPU.WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2703,16 +2509,16 @@ void ARM7Write8(u32 addr, u8 val) case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val); + GBACartSlot.SRAMWrite(addr, val); return; } - //if (ARM7->R[15] > 0x00002F30) // ARM7 BIOS bug + //if (ARM7.R[15] > 0x00002F30) // ARM7 BIOS bug if (addr >= 0x01000000) - Log(LogLevel::Debug, "unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7.R[15]); } -void ARM7Write16(u32 addr, u16 val) +void NDS::ARM7Write16(u32 addr, u16 val) { addr &= ~0x1; @@ -2720,56 +2526,46 @@ void ARM7Write16(u32 addr, u16 val) { case 0x02000000: case 0x02800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u16*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; case 0x04000000: - ARM7IOWrite16(addr, val); + NDS::ARM7IOWrite16(addr, val); return; case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return; - Wifi::Write(addr, val); + Wifi.Write(addr, val); return; } break; case 0x06000000: case 0x06800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); -#endif - GPU::WriteVRAM_ARM7(addr, val); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); + GPU.WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2777,22 +2573,22 @@ void ARM7Write16(u32 addr, u16 val) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val); + GBACartSlot.ROMWrite(addr, val); return; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, val >> 8); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, val >> 8); return; } if (addr >= 0x01000000) - Log(LogLevel::Debug, "unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7.R[15]); } -void ARM7Write32(u32 addr, u32 val) +void NDS::ARM7Write32(u32 addr, u32 val) { addr &= ~0x3; @@ -2800,57 +2596,47 @@ void ARM7Write32(u32 addr, u32 val) { case 0x02000000: case 0x02800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u32*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; case 0x04000000: - ARM7IOWrite32(addr, val); + NDS::ARM7IOWrite32(addr, val); return; case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return; - Wifi::Write(addr, val & 0xFFFF); - Wifi::Write(addr+2, val >> 16); + Wifi.Write(addr, val & 0xFFFF); + Wifi.Write(addr+2, val >> 16); return; } break; case 0x06000000: case 0x06800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); -#endif - GPU::WriteVRAM_ARM7(addr, val); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); + GPU.WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2858,25 +2644,25 @@ void ARM7Write32(u32 addr, u32 val) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val & 0xFFFF); - GBACart::ROMWrite(addr+2, val >> 16); + GBACartSlot.ROMWrite(addr, val & 0xFFFF); + GBACartSlot.ROMWrite(addr+2, val >> 16); return; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, (val >> 8) & 0xFF); - GBACart::SRAMWrite(addr+2, (val >> 16) & 0xFF); - GBACart::SRAMWrite(addr+3, val >> 24); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACartSlot.SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACartSlot.SRAMWrite(addr+3, val >> 24); return; } if (addr >= 0x01000000) - Log(LogLevel::Debug, "unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7.R[15]); } -bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) +bool NDS::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) { switch (addr & 0xFF800000) { @@ -2909,9 +2695,9 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) // BIOS. ARM7 PC has to be within range. if (addr < 0x00004000 && !write) { - if (ARM7->R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7->R[15] < ARM7BIOSProt)) + if (ARM7.R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7.R[15] < ARM7BIOSProt)) { - region->Mem = ARM7BIOS; + region->Mem = &ARM7BIOS[0]; region->Mask = 0x3FFF; return true; } @@ -2934,65 +2720,91 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) case (addr+2): return ((val) >> 16) & 0xFF; \ case (addr+3): return (val) >> 24; -u8 ARM9IORead8(u32 addr) +u8 NDS::ARM9IORead8(u32 addr) { switch (addr) { case 0x04000130: LagFrameFlag = false; return KeyInput & 0xFF; case 0x04000131: LagFrameFlag = false; return (KeyInput >> 8) & 0xFF; - case 0x04000132: return KeyCnt & 0xFF; - case 0x04000133: return KeyCnt >> 8; + case 0x04000132: return KeyCnt[0] & 0xFF; + case 0x04000133: return KeyCnt[0] >> 8; + + case 0x040001A0: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetSPICnt() & 0xFF; + return 0; + case 0x040001A1: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetSPICnt() >> 8; + return 0; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ReadSPIData(); + return NDSCartSlot.ReadSPIData(); + return 0; + + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() & 0xFF; + return 0; + case 0x040001A5: + if (!(ExMemCnt[0] & (1<<11))) + return (NDSCartSlot.GetROMCnt() >> 8) & 0xFF; + return 0; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + return (NDSCartSlot.GetROMCnt() >> 16) & 0xFF; + return 0; + case 0x040001A7: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() >> 24; return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[0]; + return NDSCartSlot.GetROMCommand(0); return 0; case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[1]; + return NDSCartSlot.GetROMCommand(1); return 0; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[2]; + return NDSCartSlot.GetROMCommand(2); return 0; case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[3]; + return NDSCartSlot.GetROMCommand(3); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[4]; + return NDSCartSlot.GetROMCommand(4); return 0; case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[5]; + return NDSCartSlot.GetROMCommand(5); return 0; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[6]; + return NDSCartSlot.GetROMCommand(6); return 0; case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[7]; + return NDSCartSlot.GetROMCommand(7); return 0; case 0x04000208: return IME[0]; - case 0x04000240: return GPU::VRAMCNT[0]; - case 0x04000241: return GPU::VRAMCNT[1]; - case 0x04000242: return GPU::VRAMCNT[2]; - case 0x04000243: return GPU::VRAMCNT[3]; - case 0x04000244: return GPU::VRAMCNT[4]; - case 0x04000245: return GPU::VRAMCNT[5]; - case 0x04000246: return GPU::VRAMCNT[6]; + case 0x04000240: return GPU.VRAMCNT[0]; + case 0x04000241: return GPU.VRAMCNT[1]; + case 0x04000242: return GPU.VRAMCNT[2]; + case 0x04000243: return GPU.VRAMCNT[3]; + case 0x04000244: return GPU.VRAMCNT[4]; + case 0x04000245: return GPU.VRAMCNT[5]; + case 0x04000246: return GPU.VRAMCNT[6]; case 0x04000247: return WRAMCnt; - case 0x04000248: return GPU::VRAMCNT[7]; - case 0x04000249: return GPU::VRAMCNT[8]; + case 0x04000248: return GPU.VRAMCNT[7]; + case 0x04000249: return GPU.VRAMCNT[8]; CASE_READ8_16BIT(0x04000280, DivCnt) CASE_READ8_32BIT(0x04000290, DivNumerator[0]) @@ -3014,49 +2826,49 @@ u8 ARM9IORead8(u32 addr) if (addr >= 0x04000000 && addr < 0x04000060) { - return GPU::GPU2D_A.Read8(addr); + return GPU.GPU2D_A.Read8(addr); } if (addr >= 0x04001000 && addr < 0x04001060) { - return GPU::GPU2D_B.Read8(addr); + return GPU.GPU2D_B.Read8(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU3D::Read8(addr); + return GPU.GPU3D.Read8(addr); } // NO$GBA debug register "Emulation ID" if(addr >= 0x04FFFA00 && addr < 0x04FFFA10) { // FIX: GBATek says this should be padded with spaces - static char const emuID[16] = "melonDS " MELONDS_VERSION; + static char const emuID[16] = "melonDS " MELONDS_VERSION_BASE; auto idx = addr - 0x04FFFA00; return (u8)(emuID[idx]); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO read8 %08X %08X\n", addr, ARM9.R[15]); return 0; } -u16 ARM9IORead16(u32 addr) +u16 NDS::ARM9IORead16(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[0]; - case 0x04000006: return GPU::VCount; + case 0x04000004: return GPU.DispStat[0]; + case 0x04000006: return GPU.VCount; - case 0x04000060: return GPU3D::Read16(addr); + case 0x04000060: return GPU.GPU3D.Read16(addr); case 0x04000064: - case 0x04000066: return GPU::GPU2D_A.Read16(addr); + case 0x04000066: return GPU.GPU2D_A.Read16(addr); - case 0x040000B8: return DMAs[0]->Cnt & 0xFFFF; - case 0x040000BA: return DMAs[0]->Cnt >> 16; - case 0x040000C4: return DMAs[1]->Cnt & 0xFFFF; - case 0x040000C6: return DMAs[1]->Cnt >> 16; - case 0x040000D0: return DMAs[2]->Cnt & 0xFFFF; - case 0x040000D2: return DMAs[2]->Cnt >> 16; - case 0x040000DC: return DMAs[3]->Cnt & 0xFFFF; - case 0x040000DE: return DMAs[3]->Cnt >> 16; + case 0x040000B8: return DMAs[0].Cnt & 0xFFFF; + case 0x040000BA: return DMAs[0].Cnt >> 16; + case 0x040000C4: return DMAs[1].Cnt & 0xFFFF; + case 0x040000C6: return DMAs[1].Cnt >> 16; + case 0x040000D0: return DMAs[2].Cnt & 0xFFFF; + case 0x040000D2: return DMAs[2].Cnt >> 16; + case 0x040000DC: return DMAs[3].Cnt & 0xFFFF; + case 0x040000DE: return DMAs[3].Cnt >> 16; case 0x040000E0: return ((u16*)DMA9Fill)[0]; case 0x040000E2: return ((u16*)DMA9Fill)[1]; @@ -3077,7 +2889,7 @@ u16 ARM9IORead16(u32 addr) case 0x0400010E: return Timers[3].Cnt; case 0x04000130: LagFrameFlag = false; return KeyInput & 0xFFFF; - case 0x04000132: return KeyCnt; + case 0x04000132: return KeyCnt[0]; case 0x04000180: return IPCSync9; case 0x04000184: @@ -3092,32 +2904,41 @@ u16 ARM9IORead16(u32 addr) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::SPICnt; + return NDSCartSlot.GetSPICnt(); return 0; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ReadSPIData(); + return NDSCartSlot.ReadSPIData(); + return 0; + + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() & 0xFFFF; + return 0; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() >> 16; return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8); return 0; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[2] | - (NDSCart::ROMCommand[3] << 8); + return NDSCartSlot.GetROMCommand(2) | + (NDSCartSlot.GetROMCommand(3) << 8); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8); return 0; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[6] | - (NDSCart::ROMCommand[7] << 8); + return NDSCartSlot.GetROMCommand(6) | + (NDSCartSlot.GetROMCommand(7) << 8); return 0; case 0x04000204: return ExMemCnt[0]; @@ -3125,11 +2946,11 @@ u16 ARM9IORead16(u32 addr) case 0x04000210: return IE[0] & 0xFFFF; case 0x04000212: return IE[0] >> 16; - case 0x04000240: return GPU::VRAMCNT[0] | (GPU::VRAMCNT[1] << 8); - case 0x04000242: return GPU::VRAMCNT[2] | (GPU::VRAMCNT[3] << 8); - case 0x04000244: return GPU::VRAMCNT[4] | (GPU::VRAMCNT[5] << 8); - case 0x04000246: return GPU::VRAMCNT[6] | (WRAMCnt << 8); - case 0x04000248: return GPU::VRAMCNT[7] | (GPU::VRAMCNT[8] << 8); + case 0x04000240: return GPU.VRAMCNT[0] | (GPU.VRAMCNT[1] << 8); + case 0x04000242: return GPU.VRAMCNT[2] | (GPU.VRAMCNT[3] << 8); + case 0x04000244: return GPU.VRAMCNT[4] | (GPU.VRAMCNT[5] << 8); + case 0x04000246: return GPU.VRAMCNT[6] | (WRAMCnt << 8); + case 0x04000248: return GPU.VRAMCNT[7] | (GPU.VRAMCNT[8] << 8); case 0x04000280: return DivCnt; case 0x04000290: return DivNumerator[0] & 0xFFFF; @@ -3169,43 +2990,43 @@ u16 ARM9IORead16(u32 addr) if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C)) { - return GPU::GPU2D_A.Read16(addr); + return GPU.GPU2D_A.Read16(addr); } if ((addr >= 0x04001000 && addr < 0x04001060) || (addr == 0x0400106C)) { - return GPU::GPU2D_B.Read16(addr); + return GPU.GPU2D_B.Read16(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU3D::Read16(addr); + return GPU.GPU3D.Read16(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM9 IO read16 %08X %08X\n", addr, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO read16 %08X %08X\n", addr, ARM9.R[15]); return 0; } -u32 ARM9IORead32(u32 addr) +u32 NDS::ARM9IORead32(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[0] | (GPU::VCount << 16); + case 0x04000004: return GPU.DispStat[0] | (GPU.VCount << 16); - case 0x04000060: return GPU3D::Read32(addr); - case 0x04000064: return GPU::GPU2D_A.Read32(addr); + case 0x04000060: return GPU.GPU3D.Read32(addr); + case 0x04000064: return GPU.GPU2D_A.Read32(addr); - case 0x040000B0: return DMAs[0]->SrcAddr; - case 0x040000B4: return DMAs[0]->DstAddr; - case 0x040000B8: return DMAs[0]->Cnt; - case 0x040000BC: return DMAs[1]->SrcAddr; - case 0x040000C0: return DMAs[1]->DstAddr; - case 0x040000C4: return DMAs[1]->Cnt; - case 0x040000C8: return DMAs[2]->SrcAddr; - case 0x040000CC: return DMAs[2]->DstAddr; - case 0x040000D0: return DMAs[2]->Cnt; - case 0x040000D4: return DMAs[3]->SrcAddr; - case 0x040000D8: return DMAs[3]->DstAddr; - case 0x040000DC: return DMAs[3]->Cnt; + case 0x040000B0: return DMAs[0].SrcAddr; + case 0x040000B4: return DMAs[0].DstAddr; + case 0x040000B8: return DMAs[0].Cnt; + case 0x040000BC: return DMAs[1].SrcAddr; + case 0x040000C0: return DMAs[1].DstAddr; + case 0x040000C4: return DMAs[1].Cnt; + case 0x040000C8: return DMAs[2].SrcAddr; + case 0x040000CC: return DMAs[2].DstAddr; + case 0x040000D0: return DMAs[2].Cnt; + case 0x040000D4: return DMAs[3].SrcAddr; + case 0x040000D8: return DMAs[3].DstAddr; + case 0x040000DC: return DMAs[3].Cnt; case 0x040000E0: return DMA9Fill[0]; case 0x040000E4: return DMA9Fill[1]; @@ -3219,42 +3040,42 @@ u32 ARM9IORead32(u32 addr) case 0x04000108: return TimerGetCounter(2) | (Timers[2].Cnt << 16); case 0x0400010C: return TimerGetCounter(3) | (Timers[3].Cnt << 16); - case 0x04000130: LagFrameFlag = false; return (KeyInput & 0xFFFF) | (KeyCnt << 16); + case 0x04000130: LagFrameFlag = false; return (KeyInput & 0xFFFF) | (KeyCnt[0] << 16); case 0x04000180: return IPCSync9; - case 0x04000184: return ARM9IORead16(addr); + case 0x04000184: return NDS::ARM9IORead16(addr); case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); + return NDSCartSlot.GetSPICnt() | (NDSCartSlot.ReadSPIData() << 16); return 0; case 0x040001A4: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCnt; + return NDSCartSlot.GetROMCnt(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8) | - (NDSCart::ROMCommand[2] << 16) | - (NDSCart::ROMCommand[3] << 24); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8) | + (NDSCartSlot.GetROMCommand(2) << 16) | + (NDSCartSlot.GetROMCommand(3) << 24); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8) | - (NDSCart::ROMCommand[6] << 16) | - (NDSCart::ROMCommand[7] << 24); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8) | + (NDSCartSlot.GetROMCommand(6) << 16) | + (NDSCartSlot.GetROMCommand(7) << 24); return 0; case 0x04000208: return IME[0]; case 0x04000210: return IE[0]; case 0x04000214: return IF[0]; - case 0x04000240: return GPU::VRAMCNT[0] | (GPU::VRAMCNT[1] << 8) | (GPU::VRAMCNT[2] << 16) | (GPU::VRAMCNT[3] << 24); - case 0x04000244: return GPU::VRAMCNT[4] | (GPU::VRAMCNT[5] << 8) | (GPU::VRAMCNT[6] << 16) | (WRAMCnt << 24); - case 0x04000248: return GPU::VRAMCNT[7] | (GPU::VRAMCNT[8] << 8); + case 0x04000240: return GPU.VRAMCNT[0] | (GPU.VRAMCNT[1] << 8) | (GPU.VRAMCNT[2] << 16) | (GPU.VRAMCNT[3] << 24); + case 0x04000244: return GPU.VRAMCNT[4] | (GPU.VRAMCNT[5] << 8) | (GPU.VRAMCNT[6] << 16) | (WRAMCnt << 24); + case 0x04000248: return GPU.VRAMCNT[7] | (GPU.VRAMCNT[8] << 8); case 0x04000280: return DivCnt; case 0x04000290: return DivNumerator[0]; @@ -3296,7 +3117,7 @@ u32 ARM9IORead32(u32 addr) return IPCFIFO7.Peek(); case 0x04100010: - if (!(ExMemCnt[0] & (1<<11))) return NDSCart::ReadROMData(); + if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot.ReadROMData(); return 0; case 0x04004000: @@ -3313,76 +3134,93 @@ u32 ARM9IORead32(u32 addr) if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C)) { - return GPU::GPU2D_A.Read32(addr); + return GPU.GPU2D_A.Read32(addr); } if ((addr >= 0x04001000 && addr < 0x04001060) || (addr == 0x0400106C)) { - return GPU::GPU2D_B.Read32(addr); + return GPU.GPU2D_B.Read32(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU3D::Read32(addr); + return GPU.GPU3D.Read32(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO read32 %08X %08X\n", addr, ARM9.R[15]); return 0; } -void ARM9IOWrite8(u32 addr, u8 val) +void NDS::ARM9IOWrite8(u32 addr, u8 val) { switch (addr) { case 0x0400006C: - case 0x0400006D: GPU::GPU2D_A.Write8(addr, val); return; + case 0x0400006D: GPU.GPU2D_A.Write8(addr, val); return; case 0x0400106C: - case 0x0400106D: GPU::GPU2D_B.Write8(addr, val); return; + case 0x0400106D: GPU.GPU2D_B.Write8(addr, val); return; case 0x04000132: - KeyCnt = (KeyCnt & 0xFF00) | val; + KeyCnt[0] = (KeyCnt[0] & 0xFF00) | val; return; case 0x04000133: - KeyCnt = (KeyCnt & 0x00FF) | (val << 8); + KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8); return; case 0x04000188: - ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); + NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0xFF00) | val); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0xFF00) | val); return; case 0x040001A1: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0x00FF) | (val << 8)); return; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPIData(val); + NDSCartSlot.WriteSPIData(val); return; - case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[0] = val; return; - case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[1] = val; return; - case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[2] = val; return; - case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[3] = val; return; - case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[4] = val; return; - case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[5] = val; return; - case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[6] = val; return; - case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[7] = val; return; + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFFFF00) | val); + return; + case 0x040001A5: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFF00FF) | (val << 8)); + return; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFF00FFFF) | (val << 16)); + return; + case 0x040001A7: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0x00FFFFFF) | (val << 24)); + return; + + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(0, val); return; + case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(1, val); return; + case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(2, val); return; + case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(3, val); return; + case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(4, val); return; + case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(5, val); return; + case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(6, val); return; + case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(7, val); return; case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; - case 0x04000240: GPU::MapVRAM_AB(0, val); return; - case 0x04000241: GPU::MapVRAM_AB(1, val); return; - case 0x04000242: GPU::MapVRAM_CD(2, val); return; - case 0x04000243: GPU::MapVRAM_CD(3, val); return; - case 0x04000244: GPU::MapVRAM_E(4, val); return; - case 0x04000245: GPU::MapVRAM_FG(5, val); return; - case 0x04000246: GPU::MapVRAM_FG(6, val); return; + case 0x04000240: GPU.MapVRAM_AB(0, val); return; + case 0x04000241: GPU.MapVRAM_AB(1, val); return; + case 0x04000242: GPU.MapVRAM_CD(2, val); return; + case 0x04000243: GPU.MapVRAM_CD(3, val); return; + case 0x04000244: GPU.MapVRAM_E(4, val); return; + case 0x04000245: GPU.MapVRAM_FG(5, val); return; + case 0x04000246: GPU.MapVRAM_FG(6, val); return; case 0x04000247: MapSharedWRAM(val); return; - case 0x04000248: GPU::MapVRAM_H(7, val); return; - case 0x04000249: GPU::MapVRAM_I(8, val); return; + case 0x04000248: GPU.MapVRAM_H(7, val); return; + case 0x04000249: GPU.MapVRAM_I(8, val); return; case 0x04000300: if (PostFlag9 & 0x01) val |= 0x01; @@ -3392,46 +3230,46 @@ void ARM9IOWrite8(u32 addr, u8 val) if (addr >= 0x04000000 && addr < 0x04000060) { - GPU::GPU2D_A.Write8(addr, val); + GPU.GPU2D_A.Write8(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU::GPU2D_B.Write8(addr, val); + GPU.GPU2D_B.Write8(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU3D::Write8(addr, val); + GPU.GPU3D.Write8(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM9 IO write8 %08X %02X %08X\n", addr, val, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO write8 %08X %02X %08X\n", addr, val, ARM9.R[15]); } -void ARM9IOWrite16(u32 addr, u16 val) +void NDS::ARM9IOWrite16(u32 addr, u16 val) { switch (addr) { - case 0x04000004: GPU::SetDispStat(0, val); return; - case 0x04000006: GPU::SetVCount(val); return; + case 0x04000004: GPU.SetDispStat(0, val); return; + case 0x04000006: GPU.SetVCount(val); return; - case 0x04000060: GPU3D::Write16(addr, val); return; + case 0x04000060: GPU.GPU3D.Write16(addr, val); return; case 0x04000068: - case 0x0400006A: GPU::GPU2D_A.Write16(addr, val); return; + case 0x0400006A: GPU.GPU2D_A.Write16(addr, val); return; - case 0x0400006C: GPU::GPU2D_A.Write16(addr, val); return; - case 0x0400106C: GPU::GPU2D_B.Write16(addr, val); return; + case 0x0400006C: GPU.GPU2D_A.Write16(addr, val); return; + case 0x0400106C: GPU.GPU2D_B.Write16(addr, val); return; - case 0x040000B8: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0xFFFF0000) | val); return; - case 0x040000BA: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000C4: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0xFFFF0000) | val); return; - case 0x040000C6: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000D0: DMAs[2]->WriteCnt((DMAs[2]->Cnt & 0xFFFF0000) | val); return; - case 0x040000D2: DMAs[2]->WriteCnt((DMAs[2]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000DC: DMAs[3]->WriteCnt((DMAs[3]->Cnt & 0xFFFF0000) | val); return; - case 0x040000DE: DMAs[3]->WriteCnt((DMAs[3]->Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000B8: DMAs[0].WriteCnt((DMAs[0].Cnt & 0xFFFF0000) | val); return; + case 0x040000BA: DMAs[0].WriteCnt((DMAs[0].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000C4: DMAs[1].WriteCnt((DMAs[1].Cnt & 0xFFFF0000) | val); return; + case 0x040000C6: DMAs[1].WriteCnt((DMAs[1].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000D0: DMAs[2].WriteCnt((DMAs[2].Cnt & 0xFFFF0000) | val); return; + case 0x040000D2: DMAs[2].WriteCnt((DMAs[2].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000DC: DMAs[3].WriteCnt((DMAs[3].Cnt & 0xFFFF0000) | val); return; + case 0x040000DE: DMAs[3].WriteCnt((DMAs[3].Cnt & 0x0000FFFF) | (val << 16)); return; case 0x040000E0: DMA9Fill[0] = (DMA9Fill[0] & 0xFFFF0000) | val; return; case 0x040000E2: DMA9Fill[0] = (DMA9Fill[0] & 0x0000FFFF) | (val << 16); return; @@ -3452,7 +3290,7 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x0400010E: TimerStart(3, val); return; case 0x04000132: - KeyCnt = val; + KeyCnt[0] = val; return; case 0x04000180: @@ -3479,44 +3317,53 @@ void ARM9IOWrite16(u32 addr, u16 val) return; case 0x04000188: - ARM9IOWrite32(addr, val | (val << 16)); + NDS::ARM9IOWrite32(addr, val | (val << 16)); return; case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPICnt(val); + NDSCartSlot.WriteSPICnt(val); return; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPIData(val & 0xFF); + NDSCartSlot.WriteSPIData(val & 0xFF); + return; + + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFF0000) | val); + return; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0x0000FFFF) | (val << 16)); return; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = val >> 8; + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, val >> 8); } return; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[2] = val & 0xFF; - NDSCart::ROMCommand[3] = val >> 8; + NDSCartSlot.SetROMCommand(2, val & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 8); } return; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = val >> 8; + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, val >> 8); } return; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[6] = val & 0xFF; - NDSCart::ROMCommand[7] = val >> 8; + NDSCartSlot.SetROMCommand(6, val & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 8); } return; @@ -3539,24 +3386,24 @@ void ARM9IOWrite16(u32 addr, u16 val) // TODO: what happens when writing to IF this way?? case 0x04000240: - GPU::MapVRAM_AB(0, val & 0xFF); - GPU::MapVRAM_AB(1, val >> 8); + GPU.MapVRAM_AB(0, val & 0xFF); + GPU.MapVRAM_AB(1, val >> 8); return; case 0x04000242: - GPU::MapVRAM_CD(2, val & 0xFF); - GPU::MapVRAM_CD(3, val >> 8); + GPU.MapVRAM_CD(2, val & 0xFF); + GPU.MapVRAM_CD(3, val >> 8); return; case 0x04000244: - GPU::MapVRAM_E(4, val & 0xFF); - GPU::MapVRAM_FG(5, val >> 8); + GPU.MapVRAM_E(4, val & 0xFF); + GPU.MapVRAM_FG(5, val >> 8); return; case 0x04000246: - GPU::MapVRAM_FG(6, val & 0xFF); + GPU.MapVRAM_FG(6, val & 0xFF); MapSharedWRAM(val >> 8); return; case 0x04000248: - GPU::MapVRAM_H(7, val & 0xFF); - GPU::MapVRAM_I(8, val >> 8); + GPU.MapVRAM_H(7, val & 0xFF); + GPU.MapVRAM_I(8, val >> 8); return; case 0x04000280: DivCnt = val; StartDiv(); return; @@ -3570,57 +3417,57 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x04000304: PowerControl9 = val & 0x820F; - GPU::SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); return; } if (addr >= 0x04000000 && addr < 0x04000060) { - GPU::GPU2D_A.Write16(addr, val); + GPU.GPU2D_A.Write16(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU::GPU2D_B.Write16(addr, val); + GPU.GPU2D_B.Write16(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU3D::Write16(addr, val); + GPU.GPU3D.Write16(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM9 IO write16 %08X %04X %08X\n", addr, val, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO write16 %08X %04X %08X\n", addr, val, ARM9.R[15]); } -void ARM9IOWrite32(u32 addr, u32 val) +void NDS::ARM9IOWrite32(u32 addr, u32 val) { switch (addr) { case 0x04000004: - GPU::SetDispStat(0, val & 0xFFFF); - GPU::SetVCount(val >> 16); + GPU.SetDispStat(0, val & 0xFFFF); + GPU.SetVCount(val >> 16); return; - case 0x04000060: GPU3D::Write32(addr, val); return; + case 0x04000060: GPU.GPU3D.Write32(addr, val); return; case 0x04000064: - case 0x04000068: GPU::GPU2D_A.Write32(addr, val); return; + case 0x04000068: GPU.GPU2D_A.Write32(addr, val); return; - case 0x0400006C: GPU::GPU2D_A.Write16(addr, val&0xFFFF); return; - case 0x0400106C: GPU::GPU2D_B.Write16(addr, val&0xFFFF); return; + case 0x0400006C: GPU.GPU2D_A.Write16(addr, val&0xFFFF); return; + case 0x0400106C: GPU.GPU2D_B.Write16(addr, val&0xFFFF); return; - case 0x040000B0: DMAs[0]->SrcAddr = val; return; - case 0x040000B4: DMAs[0]->DstAddr = val; return; - case 0x040000B8: DMAs[0]->WriteCnt(val); return; - case 0x040000BC: DMAs[1]->SrcAddr = val; return; - case 0x040000C0: DMAs[1]->DstAddr = val; return; - case 0x040000C4: DMAs[1]->WriteCnt(val); return; - case 0x040000C8: DMAs[2]->SrcAddr = val; return; - case 0x040000CC: DMAs[2]->DstAddr = val; return; - case 0x040000D0: DMAs[2]->WriteCnt(val); return; - case 0x040000D4: DMAs[3]->SrcAddr = val; return; - case 0x040000D8: DMAs[3]->DstAddr = val; return; - case 0x040000DC: DMAs[3]->WriteCnt(val); return; + case 0x040000B0: DMAs[0].SrcAddr = val; return; + case 0x040000B4: DMAs[0].DstAddr = val; return; + case 0x040000B8: DMAs[0].WriteCnt(val); return; + case 0x040000BC: DMAs[1].SrcAddr = val; return; + case 0x040000C0: DMAs[1].DstAddr = val; return; + case 0x040000C4: DMAs[1].WriteCnt(val); return; + case 0x040000C8: DMAs[2].SrcAddr = val; return; + case 0x040000CC: DMAs[2].DstAddr = val; return; + case 0x040000D0: DMAs[2].WriteCnt(val); return; + case 0x040000D4: DMAs[3].SrcAddr = val; return; + case 0x040000D8: DMAs[3].DstAddr = val; return; + case 0x040000DC: DMAs[3].WriteCnt(val); return; case 0x040000E0: DMA9Fill[0] = val; return; case 0x040000E4: DMA9Fill[1] = val; return; @@ -3645,12 +3492,12 @@ void ARM9IOWrite32(u32 addr, u32 val) return; case 0x04000130: - KeyCnt = val >> 16; + KeyCnt[0] = val >> 16; return; case 0x04000180: case 0x04000184: - ARM9IOWrite16(addr, val); + NDS::ARM9IOWrite16(addr, val); return; case 0x04000188: if (IPCFIFOCnt9 & 0x8000) @@ -3670,31 +3517,31 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::WriteSPICnt(val & 0xFFFF); - NDSCart::WriteSPIData((val >> 16) & 0xFF); + NDSCartSlot.WriteSPICnt(val & 0xFFFF); + NDSCartSlot.WriteSPIData((val >> 16) & 0xFF); } return; case 0x040001A4: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteROMCnt(val); + NDSCartSlot.WriteROMCnt(val); return; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[3] = val >> 24; + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(2, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 24); } return; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[7] = val >> 24; + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(6, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 24); } return; @@ -3703,23 +3550,23 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; case 0x04000210: IE[0] = val; UpdateIRQ(0); return; - case 0x04000214: IF[0] &= ~val; GPU3D::CheckFIFOIRQ(); UpdateIRQ(0); return; + case 0x04000214: IF[0] &= ~val; GPU.GPU3D.CheckFIFOIRQ(); UpdateIRQ(0); return; case 0x04000240: - GPU::MapVRAM_AB(0, val & 0xFF); - GPU::MapVRAM_AB(1, (val >> 8) & 0xFF); - GPU::MapVRAM_CD(2, (val >> 16) & 0xFF); - GPU::MapVRAM_CD(3, val >> 24); + GPU.MapVRAM_AB(0, val & 0xFF); + GPU.MapVRAM_AB(1, (val >> 8) & 0xFF); + GPU.MapVRAM_CD(2, (val >> 16) & 0xFF); + GPU.MapVRAM_CD(3, val >> 24); return; case 0x04000244: - GPU::MapVRAM_E(4, val & 0xFF); - GPU::MapVRAM_FG(5, (val >> 8) & 0xFF); - GPU::MapVRAM_FG(6, (val >> 16) & 0xFF); + GPU.MapVRAM_E(4, val & 0xFF); + GPU.MapVRAM_FG(5, (val >> 8) & 0xFF); + GPU.MapVRAM_FG(6, (val >> 16) & 0xFF); MapSharedWRAM(val >> 24); return; case 0x04000248: - GPU::MapVRAM_H(7, val & 0xFF); - GPU::MapVRAM_I(8, (val >> 8) & 0xFF); + GPU.MapVRAM_H(7, val & 0xFF); + GPU.MapVRAM_I(8, (val >> 8) & 0xFF); return; case 0x04000280: DivCnt = val; StartDiv(); return; @@ -3736,11 +3583,11 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000304: PowerControl9 = val & 0x820F; - GPU::SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); return; case 0x04100010: - if (!(ExMemCnt[0] & (1<<11))) NDSCart::WriteROMData(val); + if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.WriteROMData(val); return; // NO$GBA debug register "String Out (raw)" @@ -3774,82 +3621,108 @@ void ARM9IOWrite32(u32 addr, u32 val) if (addr >= 0x04000000 && addr < 0x04000060) { - GPU::GPU2D_A.Write32(addr, val); + GPU.GPU2D_A.Write32(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU::GPU2D_B.Write32(addr, val); + GPU.GPU2D_B.Write32(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU3D::Write32(addr, val); + GPU.GPU3D.Write32(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM9 IO write32 %08X %08X %08X\n", addr, val, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO write32 %08X %08X %08X\n", addr, val, ARM9.R[15]); } -u8 ARM7IORead8(u32 addr) +u8 NDS::ARM7IORead8(u32 addr) { switch (addr) { case 0x04000130: return KeyInput & 0xFF; case 0x04000131: return (KeyInput >> 8) & 0xFF; - case 0x04000132: return KeyCnt & 0xFF; - case 0x04000133: return KeyCnt >> 8; + case 0x04000132: return KeyCnt[1] & 0xFF; + case 0x04000133: return KeyCnt[1] >> 8; case 0x04000134: return RCnt & 0xFF; case 0x04000135: return RCnt >> 8; case 0x04000136: return (KeyInput >> 16) & 0xFF; case 0x04000137: return KeyInput >> 24; - case 0x04000138: return RTC::Read() & 0xFF; + case 0x04000138: return RTC.Read() & 0xFF; + + case 0x040001A0: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetSPICnt() & 0xFF; + return 0; + case 0x040001A1: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetSPICnt() >> 8; + return 0; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ReadSPIData(); + return NDSCartSlot.ReadSPIData(); + return 0; + + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() & 0xFF; + return 0; + case 0x040001A5: + if (ExMemCnt[0] & (1<<11)) + return (NDSCartSlot.GetROMCnt() >> 8) & 0xFF; + return 0; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + return (NDSCartSlot.GetROMCnt() >> 16) & 0xFF; + return 0; + case 0x040001A7: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() >> 24; return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[0]; + return NDSCartSlot.GetROMCommand(0); return 0; case 0x040001A9: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[1]; + return NDSCartSlot.GetROMCommand(1); return 0; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[2]; + return NDSCartSlot.GetROMCommand(2); return 0; case 0x040001AB: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[3]; + return NDSCartSlot.GetROMCommand(3); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[4]; + return NDSCartSlot.GetROMCommand(4); return 0; case 0x040001AD: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[5]; + return NDSCartSlot.GetROMCommand(5); return 0; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[6]; + return NDSCartSlot.GetROMCommand(6); return 0; case 0x040001AF: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[7]; + return NDSCartSlot.GetROMCommand(7); return 0; - case 0x040001C2: return SPI::ReadData(); + case 0x040001C2: return SPI.ReadData(); case 0x04000208: return IME[1]; - case 0x04000240: return GPU::VRAMSTAT; + case 0x04000240: return GPU.VRAMSTAT; case 0x04000241: return WRAMCnt; case 0x04000300: return PostFlag7; @@ -3858,29 +3731,29 @@ u8 ARM7IORead8(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU::Read8(addr); + return SPU.Read8(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO read8 %08X %08X\n", addr, ARM7.R[15]); return 0; } -u16 ARM7IORead16(u32 addr) +u16 NDS::ARM7IORead16(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[1]; - case 0x04000006: return GPU::VCount; + case 0x04000004: return GPU.DispStat[1]; + case 0x04000006: return GPU.VCount; - case 0x040000B8: return DMAs[4]->Cnt & 0xFFFF; - case 0x040000BA: return DMAs[4]->Cnt >> 16; - case 0x040000C4: return DMAs[5]->Cnt & 0xFFFF; - case 0x040000C6: return DMAs[5]->Cnt >> 16; - case 0x040000D0: return DMAs[6]->Cnt & 0xFFFF; - case 0x040000D2: return DMAs[6]->Cnt >> 16; - case 0x040000DC: return DMAs[7]->Cnt & 0xFFFF; - case 0x040000DE: return DMAs[7]->Cnt >> 16; + case 0x040000B8: return DMAs[4].Cnt & 0xFFFF; + case 0x040000BA: return DMAs[4].Cnt >> 16; + case 0x040000C4: return DMAs[5].Cnt & 0xFFFF; + case 0x040000C6: return DMAs[5].Cnt >> 16; + case 0x040000D0: return DMAs[6].Cnt & 0xFFFF; + case 0x040000D2: return DMAs[6].Cnt >> 16; + case 0x040000DC: return DMAs[7].Cnt & 0xFFFF; + case 0x040000DE: return DMAs[7].Cnt >> 16; case 0x04000100: return TimerGetCounter(4); case 0x04000102: return Timers[4].Cnt; @@ -3892,11 +3765,11 @@ u16 ARM7IORead16(u32 addr) case 0x0400010E: return Timers[7].Cnt; case 0x04000130: return KeyInput & 0xFFFF; - case 0x04000132: return KeyCnt; + case 0x04000132: return KeyCnt[1]; case 0x04000134: return RCnt; case 0x04000136: return KeyInput >> 16; - case 0x04000138: return RTC::Read(); + case 0x04000138: return RTC.Read(); case 0x04000180: return IPCSync7; case 0x04000184: @@ -3909,32 +3782,41 @@ u16 ARM7IORead16(u32 addr) return val; } - case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCart::SPICnt; return 0; - case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCart::ReadSPIData(); return 0; + case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.GetSPICnt(); return 0; + case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.ReadSPIData(); return 0; + + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() & 0xFFFF; + return 0; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() >> 16; + return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8); return 0; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[2] | - (NDSCart::ROMCommand[3] << 8); + return NDSCartSlot.GetROMCommand(2) | + (NDSCartSlot.GetROMCommand(3) << 8); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8); return 0; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[6] | - (NDSCart::ROMCommand[7] << 8); + return NDSCartSlot.GetROMCommand(6) | + (NDSCartSlot.GetROMCommand(7) << 8); return 0; - case 0x040001C0: return SPI::Cnt; - case 0x040001C2: return SPI::ReadData(); + case 0x040001C0: return SPI.ReadCnt(); + case 0x040001C2: return SPI.ReadData(); case 0x04000204: return ExMemCnt[1]; case 0x04000206: @@ -3952,71 +3834,71 @@ u16 ARM7IORead16(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU::Read16(addr); + return SPU.Read16(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO read16 %08X %08X\n", addr, ARM7.R[15]); return 0; } -u32 ARM7IORead32(u32 addr) +u32 NDS::ARM7IORead32(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[1] | (GPU::VCount << 16); + case 0x04000004: return GPU.DispStat[1] | (GPU.VCount << 16); - case 0x040000B0: return DMAs[4]->SrcAddr; - case 0x040000B4: return DMAs[4]->DstAddr; - case 0x040000B8: return DMAs[4]->Cnt; - case 0x040000BC: return DMAs[5]->SrcAddr; - case 0x040000C0: return DMAs[5]->DstAddr; - case 0x040000C4: return DMAs[5]->Cnt; - case 0x040000C8: return DMAs[6]->SrcAddr; - case 0x040000CC: return DMAs[6]->DstAddr; - case 0x040000D0: return DMAs[6]->Cnt; - case 0x040000D4: return DMAs[7]->SrcAddr; - case 0x040000D8: return DMAs[7]->DstAddr; - case 0x040000DC: return DMAs[7]->Cnt; + case 0x040000B0: return DMAs[4].SrcAddr; + case 0x040000B4: return DMAs[4].DstAddr; + case 0x040000B8: return DMAs[4].Cnt; + case 0x040000BC: return DMAs[5].SrcAddr; + case 0x040000C0: return DMAs[5].DstAddr; + case 0x040000C4: return DMAs[5].Cnt; + case 0x040000C8: return DMAs[6].SrcAddr; + case 0x040000CC: return DMAs[6].DstAddr; + case 0x040000D0: return DMAs[6].Cnt; + case 0x040000D4: return DMAs[7].SrcAddr; + case 0x040000D8: return DMAs[7].DstAddr; + case 0x040000DC: return DMAs[7].Cnt; case 0x04000100: return TimerGetCounter(4) | (Timers[4].Cnt << 16); case 0x04000104: return TimerGetCounter(5) | (Timers[5].Cnt << 16); case 0x04000108: return TimerGetCounter(6) | (Timers[6].Cnt << 16); case 0x0400010C: return TimerGetCounter(7) | (Timers[7].Cnt << 16); - case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt << 16); - case 0x04000134: return RCnt | (KeyCnt & 0xFFFF0000); - case 0x04000138: return RTC::Read(); + case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt[1] << 16); + case 0x04000134: return RCnt | (KeyInput & 0xFFFF0000); + case 0x04000138: return RTC.Read(); case 0x04000180: return IPCSync7; - case 0x04000184: return ARM7IORead16(addr); + case 0x04000184: return NDS::ARM7IORead16(addr); case 0x040001A0: if (ExMemCnt[0] & (1<<11)) - return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); + return NDSCartSlot.GetSPICnt() | (NDSCartSlot.ReadSPIData() << 16); return 0; case 0x040001A4: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCnt; + return NDSCartSlot.GetROMCnt(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8) | - (NDSCart::ROMCommand[2] << 16) | - (NDSCart::ROMCommand[3] << 24); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8) | + (NDSCartSlot.GetROMCommand(2) << 16) | + (NDSCartSlot.GetROMCommand(3) << 24); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8) | - (NDSCart::ROMCommand[6] << 16) | - (NDSCart::ROMCommand[7] << 24); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8) | + (NDSCartSlot.GetROMCommand(6) << 16) | + (NDSCartSlot.GetROMCommand(7) << 24); return 0; case 0x040001C0: - return SPI::Cnt | (SPI::ReadData() << 16); + return SPI.ReadCnt() | (SPI.ReadData() << 16); case 0x04000208: return IME[1]; case 0x04000210: return IE[1]; @@ -4047,29 +3929,29 @@ u32 ARM7IORead32(u32 addr) return IPCFIFO9.Peek(); case 0x04100010: - if (ExMemCnt[0] & (1<<11)) return NDSCart::ReadROMData(); + if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.ReadROMData(); return 0; } if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU::Read32(addr); + return SPU.Read32(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO read32 %08X %08X\n", addr, ARM7.R[15]); return 0; } -void ARM7IOWrite8(u32 addr, u8 val) +void NDS::ARM7IOWrite8(u32 addr, u8 val) { switch (addr) { case 0x04000132: - KeyCnt = (KeyCnt & 0xFF00) | val; + KeyCnt[1] = (KeyCnt[1] & 0xFF00) | val; return; case 0x04000133: - KeyCnt = (KeyCnt & 0x00FF) | (val << 8); + KeyCnt[1] = (KeyCnt[1] & 0x00FF) | (val << 8); return; case 0x04000134: RCnt = (RCnt & 0xFF00) | val; @@ -4078,44 +3960,61 @@ void ARM7IOWrite8(u32 addr, u8 val) RCnt = (RCnt & 0x00FF) | (val << 8); return; - case 0x04000138: RTC::Write(val, true); return; + case 0x04000138: RTC.Write(val, true); return; case 0x04000188: - ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); + NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; case 0x040001A0: if (ExMemCnt[0] & (1<<11)) { - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0xFF00) | val); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0xFF00) | val); } return; case 0x040001A1: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0x00FF) | (val << 8)); return; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPIData(val); + NDSCartSlot.WriteSPIData(val); return; - case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[0] = val; return; - case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[1] = val; return; - case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[2] = val; return; - case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[3] = val; return; - case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[4] = val; return; - case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[5] = val; return; - case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[6] = val; return; - case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[7] = val; return; + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFFFF00) | val); + return; + case 0x040001A5: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFF00FF) | (val << 8)); + return; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFF00FFFF) | (val << 16)); + return; + case 0x040001A7: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0x00FFFFFF) | (val << 24)); + return; + + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(0, val); return; + case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(1, val); return; + case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(2, val); return; + case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(3, val); return; + case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(4, val); return; + case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(5, val); return; + case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(6, val); return; + case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(7, val); return; case 0x040001C2: - SPI::WriteData(val); + SPI.WriteData(val); return; case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return; case 0x04000300: - if (ARM7->R[15] >= 0x4000) + if (ARM7.R[15] >= 0x4000) return; if (!(PostFlag7 & 0x01)) PostFlag7 = val & 0x01; @@ -4124,35 +4023,35 @@ void ARM7IOWrite8(u32 addr, u8 val) case 0x04000301: val &= 0xC0; if (val == 0x40) Stop(StopReason::GBAModeNotSupported); - else if (val == 0x80) ARM7->Halt(1); + else if (val == 0x80) ARM7.Halt(1); else if (val == 0xC0) EnterSleepMode(); return; } if (addr >= 0x04000400 && addr < 0x04000520) { - SPU::Write8(addr, val); + SPU.Write8(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM7 IO write8 %08X %02X %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO write8 %08X %02X %08X\n", addr, val, ARM7.R[15]); } -void ARM7IOWrite16(u32 addr, u16 val) +void NDS::ARM7IOWrite16(u32 addr, u16 val) { switch (addr) { - case 0x04000004: GPU::SetDispStat(1, val); return; - case 0x04000006: GPU::SetVCount(val); return; + case 0x04000004: GPU.SetDispStat(1, val); return; + case 0x04000006: GPU.SetVCount(val); return; - case 0x040000B8: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0xFFFF0000) | val); return; - case 0x040000BA: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000C4: DMAs[5]->WriteCnt((DMAs[5]->Cnt & 0xFFFF0000) | val); return; - case 0x040000C6: DMAs[5]->WriteCnt((DMAs[5]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000D0: DMAs[6]->WriteCnt((DMAs[6]->Cnt & 0xFFFF0000) | val); return; - case 0x040000D2: DMAs[6]->WriteCnt((DMAs[6]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000DC: DMAs[7]->WriteCnt((DMAs[7]->Cnt & 0xFFFF0000) | val); return; - case 0x040000DE: DMAs[7]->WriteCnt((DMAs[7]->Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000B8: DMAs[4].WriteCnt((DMAs[4].Cnt & 0xFFFF0000) | val); return; + case 0x040000BA: DMAs[4].WriteCnt((DMAs[4].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000C4: DMAs[5].WriteCnt((DMAs[5].Cnt & 0xFFFF0000) | val); return; + case 0x040000C6: DMAs[5].WriteCnt((DMAs[5].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000D0: DMAs[6].WriteCnt((DMAs[6].Cnt & 0xFFFF0000) | val); return; + case 0x040000D2: DMAs[6].WriteCnt((DMAs[6].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000DC: DMAs[7].WriteCnt((DMAs[7].Cnt & 0xFFFF0000) | val); return; + case 0x040000DE: DMAs[7].WriteCnt((DMAs[7].Cnt & 0x0000FFFF) | (val << 16)); return; case 0x04000100: Timers[4].Reload = val; return; case 0x04000102: TimerStart(4, val); return; @@ -4163,10 +4062,10 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x0400010C: Timers[7].Reload = val; return; case 0x0400010E: TimerStart(7, val); return; - case 0x04000132: KeyCnt = val; return; + case 0x04000132: KeyCnt[1] = val; return; case 0x04000134: RCnt = val; return; - case 0x04000138: RTC::Write(val, false); return; + case 0x04000138: RTC.Write(val, false); return; case 0x04000180: IPCSync9 &= 0xFFF0; @@ -4192,44 +4091,53 @@ void ARM7IOWrite16(u32 addr, u16 val) return; case 0x04000188: - ARM7IOWrite32(addr, val | (val << 16)); + NDS::ARM7IOWrite32(addr, val | (val << 16)); return; case 0x040001A0: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPICnt(val); + NDSCartSlot.WriteSPICnt(val); return; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPIData(val & 0xFF); + NDSCartSlot.WriteSPIData(val & 0xFF); + return; + + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFFFF00) | val); + return; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFF00FFFF) | (val << 16)); return; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = val >> 8; + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, val >> 8); } return; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[2] = val & 0xFF; - NDSCart::ROMCommand[3] = val >> 8; + NDSCartSlot.SetROMCommand(2, val & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 8); } return; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = val >> 8; + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, val >> 8); } return; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[6] = val & 0xFF; - NDSCart::ROMCommand[7] = val >> 8; + NDSCartSlot.SetROMCommand(6, val & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 8); } return; @@ -4237,10 +4145,10 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x040001BA: ROMSeed1[12] = val & 0x7F; return; case 0x040001C0: - SPI::WriteCnt(val); + SPI.WriteCnt(val); return; case 0x040001C2: - SPI::WriteData(val & 0xFF); + SPI.WriteData(val & 0xFF); return; case 0x04000204: @@ -4262,7 +4170,7 @@ void ARM7IOWrite16(u32 addr, u16 val) // TODO: what happens when writing to IF this way?? case 0x04000300: - if (ARM7->R[15] >= 0x4000) + if (ARM7.R[15] >= 0x4000) return; if (!(PostFlag7 & 0x01)) PostFlag7 = val & 0x01; @@ -4272,8 +4180,8 @@ void ARM7IOWrite16(u32 addr, u16 val) { u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; - SPU::SetPowerCnt(val & 0x0001); - Wifi::SetPowerCnt(val & 0x0002); + SPU.SetPowerCnt(val & 0x0001); + Wifi.SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } return; @@ -4286,34 +4194,34 @@ void ARM7IOWrite16(u32 addr, u16 val) if (addr >= 0x04000400 && addr < 0x04000520) { - SPU::Write16(addr, val); + SPU.Write16(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM7 IO write16 %08X %04X %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO write16 %08X %04X %08X\n", addr, val, ARM7.R[15]); } -void ARM7IOWrite32(u32 addr, u32 val) +void NDS::ARM7IOWrite32(u32 addr, u32 val) { switch (addr) { case 0x04000004: - GPU::SetDispStat(1, val & 0xFFFF); - GPU::SetVCount(val >> 16); + GPU.SetDispStat(1, val & 0xFFFF); + GPU.SetVCount(val >> 16); return; - case 0x040000B0: DMAs[4]->SrcAddr = val; return; - case 0x040000B4: DMAs[4]->DstAddr = val; return; - case 0x040000B8: DMAs[4]->WriteCnt(val); return; - case 0x040000BC: DMAs[5]->SrcAddr = val; return; - case 0x040000C0: DMAs[5]->DstAddr = val; return; - case 0x040000C4: DMAs[5]->WriteCnt(val); return; - case 0x040000C8: DMAs[6]->SrcAddr = val; return; - case 0x040000CC: DMAs[6]->DstAddr = val; return; - case 0x040000D0: DMAs[6]->WriteCnt(val); return; - case 0x040000D4: DMAs[7]->SrcAddr = val; return; - case 0x040000D8: DMAs[7]->DstAddr = val; return; - case 0x040000DC: DMAs[7]->WriteCnt(val); return; + case 0x040000B0: DMAs[4].SrcAddr = val; return; + case 0x040000B4: DMAs[4].DstAddr = val; return; + case 0x040000B8: DMAs[4].WriteCnt(val); return; + case 0x040000BC: DMAs[5].SrcAddr = val; return; + case 0x040000C0: DMAs[5].DstAddr = val; return; + case 0x040000C4: DMAs[5].WriteCnt(val); return; + case 0x040000C8: DMAs[6].SrcAddr = val; return; + case 0x040000CC: DMAs[6].DstAddr = val; return; + case 0x040000D0: DMAs[6].WriteCnt(val); return; + case 0x040000D4: DMAs[7].SrcAddr = val; return; + case 0x040000D8: DMAs[7].DstAddr = val; return; + case 0x040000DC: DMAs[7].WriteCnt(val); return; case 0x04000100: Timers[4].Reload = val & 0xFFFF; @@ -4332,13 +4240,13 @@ void ARM7IOWrite32(u32 addr, u32 val) TimerStart(7, val>>16); return; - case 0x04000130: KeyCnt = val >> 16; return; + case 0x04000130: KeyCnt[1] = val >> 16; return; case 0x04000134: RCnt = val & 0xFFFF; return; - case 0x04000138: RTC::Write(val & 0xFFFF, false); return; + case 0x04000138: RTC.Write(val & 0xFFFF, false); return; case 0x04000180: case 0x04000184: - ARM7IOWrite16(addr, val); + NDS::ARM7IOWrite16(addr, val); return; case 0x04000188: if (IPCFIFOCnt7 & 0x8000) @@ -4358,31 +4266,31 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) { - NDSCart::WriteSPICnt(val & 0xFFFF); - NDSCart::WriteSPIData((val >> 16) & 0xFF); + NDSCartSlot.WriteSPICnt(val & 0xFFFF); + NDSCartSlot.WriteSPIData((val >> 16) & 0xFF); } return; case 0x040001A4: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteROMCnt(val); + NDSCartSlot.WriteROMCnt(val); return; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[3] = val >> 24; + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(2, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 24); } return; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[7] = val >> 24; + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(6, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 24); } return; @@ -4390,8 +4298,8 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x040001B4: *(u32*)&ROMSeed1[8] = val; return; case 0x040001C0: - SPI::WriteCnt(val & 0xFFFF); - SPI::WriteData((val >> 16) & 0xFF); + SPI.WriteCnt(val & 0xFFFF); + SPI.WriteData((val >> 16) & 0xFF); return; case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return; @@ -4402,8 +4310,8 @@ void ARM7IOWrite32(u32 addr, u32 val) { u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; - SPU::SetPowerCnt(val & 0x0001); - Wifi::SetPowerCnt(val & 0x0002); + SPU.SetPowerCnt(val & 0x0001); + Wifi.SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } return; @@ -4414,17 +4322,17 @@ void ARM7IOWrite32(u32 addr, u32 val) return; case 0x04100010: - if (ExMemCnt[0] & (1<<11)) NDSCart::WriteROMData(val); + if (ExMemCnt[0] & (1<<11)) NDSCartSlot.WriteROMData(val); return; } if (addr >= 0x04000400 && addr < 0x04000520) { - SPU::Write32(addr, val); + SPU.Write32(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7.R[15]); } } diff --git a/src/NDS.h b/src/NDS.h index e81e952b..f9df2d69 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,24 +19,44 @@ #ifndef NDS_H #define NDS_H +#include #include +#include +#include #include "Platform.h" #include "Savestate.h" #include "types.h" +#include "NDSCart.h" +#include "GBACart.h" +#include "SPU.h" +#include "SPI.h" +#include "RTC.h" +#include "Wifi.h" +#include "AREngine.h" +#include "GPU.h" +#include "ARMJIT.h" +#include "MemRegion.h" +#include "ARMJIT_Memory.h" +#include "ARM.h" +#include "CRC32.h" +#include "DMA.h" +#include "FreeBIOS.h" // when touching the main loop/timing code, pls test a lot of shit // with this enabled, to make sure it doesn't desync //#define DEBUG_CHECK_DESYNC -namespace NDS +namespace melonDS { - +struct NDSArgs; +class Firmware; enum { Event_LCD = 0, Event_SPU, Event_Wifi, + Event_RTC, Event_DisplayFIFO, Event_ROMTransfer, @@ -56,13 +76,15 @@ enum Event_MAX }; +typedef std::function EventFunc; +#define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1) struct SchedEvent { - void (*Func)(u32 param); + std::map Funcs; u64 Timestamp; + u32 FuncID; u32 Param; }; - enum { IRQ_VBlank = 0, @@ -122,6 +144,33 @@ enum IRQ2_DSi_MicExt }; +enum +{ + CPUStop_DMA9_0 = (1<<0), + CPUStop_DMA9_1 = (1<<1), + CPUStop_DMA9_2 = (1<<2), + CPUStop_DMA9_3 = (1<<3), + CPUStop_NDMA9_0 = (1<<4), + CPUStop_NDMA9_1 = (1<<5), + CPUStop_NDMA9_2 = (1<<6), + CPUStop_NDMA9_3 = (1<<7), + CPUStop_DMA9 = 0xFFF, + + CPUStop_DMA7_0 = (1<<16), + CPUStop_DMA7_1 = (1<<17), + CPUStop_DMA7_2 = (1<<18), + CPUStop_DMA7_3 = (1<<19), + CPUStop_NDMA7_0 = (1<<20), + CPUStop_NDMA7_1 = (1<<21), + CPUStop_NDMA7_2 = (1<<22), + CPUStop_NDMA7_3 = (1<<23), + CPUStop_DMA7 = (0xFFF<<16), + + CPUStop_Wakeup = (1<<29), + CPUStop_Sleep = (1<<30), + CPUStop_GXStall = (1<<31), +}; + struct Timer { u16 Reload; @@ -157,183 +206,335 @@ enum // TODO: add DSi regions! }; -struct MemRegion -{ - u8* Mem; - u32 Mask; -}; - // supported GBA slot addon types enum { GBAAddon_RAMExpansion = 1, }; +class SPU; +class SPIHost; +class RTC; +class Wifi; + +class AREngine; +class GPU; +class ARMJIT; + +class NDS +{ +private: #ifdef JIT_ENABLED -extern bool EnableJIT; + bool EnableJIT; #endif -extern int ConsoleType; -extern int CurCPU; -extern u8 ARM9MemTimings[0x40000][8]; -extern u32 ARM9Regions[0x40000]; -extern u8 ARM7MemTimings[0x20000][4]; -extern u32 ARM7Regions[0x20000]; +public: // TODO: Encapsulate the rest of these members + int ConsoleType; + int CurCPU; -extern u32 NumFrames; -extern u32 NumLagFrames; -extern bool LagFrameFlag; + SchedEvent SchedList[Event_MAX] {}; + u8 ARM9MemTimings[0x40000][8]; + u32 ARM9Regions[0x40000]; + u8 ARM7MemTimings[0x20000][4]; + u32 ARM7Regions[0x20000]; -extern u64 ARM9Timestamp, ARM9Target; -extern u64 ARM7Timestamp, ARM7Target; -extern u32 ARM9ClockShift; + u32 NumFrames; + u32 NumLagFrames; + bool LagFrameFlag; -extern u32 IME[2]; -extern u32 IE[2]; -extern u32 IF[2]; -extern u32 IE2; -extern u32 IF2; -extern Timer Timers[8]; + // no need to worry about those overflowing, they can keep going for atleast 4350 years + u64 ARM9Timestamp, ARM9Target; + u64 ARM7Timestamp, ARM7Target; + u32 ARM9ClockShift; -extern u32 CPUStop; + u32 IME[2]; + u32 IE[2]; + u32 IF[2]; + u32 IE2; + u32 IF2; + Timer Timers[8]; -extern u16 PowerControl9; + u32 CPUStop; -extern u16 ExMemCnt[2]; -extern u8 ROMSeed0[2*8]; -extern u8 ROMSeed1[2*8]; + u16 PowerControl9; -extern u8 ARM9BIOS[0x1000]; -extern u8 ARM7BIOS[0x4000]; -extern u16 ARM7BIOSProt; + u16 ExMemCnt[2]; + alignas(u32) u8 ROMSeed0[2*8]; + alignas(u32) u8 ROMSeed1[2*8]; -extern u8* MainRAM; -extern u32 MainRAMMask; +protected: + // These BIOS arrays should be declared *before* the component objects (JIT, SPI, etc.) + // so that they're initialized before the component objects' constructors run. + std::array ARM9BIOS; + std::array ARM7BIOS; + bool ARM9BIOSNative; + bool ARM7BIOSNative; +public: // TODO: Encapsulate the rest of these members + u16 ARM7BIOSProt; -const u32 MainRAMMaxSize = 0x1000000; + u8* MainRAM; + u32 MainRAMMask; -const u32 SharedWRAMSize = 0x8000; -extern u8* SharedWRAM; + const u32 MainRAMMaxSize = 0x1000000; -extern MemRegion SWRAM_ARM9; -extern MemRegion SWRAM_ARM7; + const u32 SharedWRAMSize = 0x8000; + u8* SharedWRAM; -extern u32 KeyInput; + MemRegion SWRAM_ARM9; + MemRegion SWRAM_ARM7; -const u32 ARM7WRAMSize = 0x10000; -extern u8* ARM7WRAM; + u32 KeyInput; + u16 RCnt; -bool Init(); -void DeInit(); -void Reset(); -void Start(); + // JIT MUST be declared before all other component objects, + // as they'll need the memory that it allocates in its constructor! + // (Reminder: C++ fields are initialized in the order they're declared, + // regardless of what the constructor's initializer list says.) + melonDS::ARMJIT JIT; + ARMv5 ARM9; + ARMv4 ARM7; + melonDS::SPU SPU; + SPIHost SPI; + melonDS::RTC RTC; + melonDS::Wifi Wifi; + NDSCart::NDSCartSlot NDSCartSlot; + GBACart::GBACartSlot GBACartSlot; + melonDS::GPU GPU; + melonDS::AREngine AREngine; -/// Stop the emulator. -void Stop(Platform::StopReason reason = Platform::StopReason::External); + const u32 ARM7WRAMSize = 0x10000; + u8* ARM7WRAM; -bool DoSavestate(Savestate* file); + virtual void Reset(); + void Start(); -void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); -void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); + /// Stop the emulator. + virtual void Stop(Platform::StopReason reason = Platform::StopReason::External); -// 0=DS 1=DSi -void SetConsoleType(int type); + bool DoSavestate(Savestate* file); -void LoadBIOS(); -bool IsLoadedARM9BIOSBuiltIn(); -bool IsLoadedARM7BIOSBuiltIn(); + void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); + void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); -bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); -void LoadSave(const u8* savedata, u32 savelen); -void EjectCart(); -bool CartInserted(); + void LoadBIOS(); -bool NeedsDirectBoot(); -void SetupDirectBoot(const std::string& romname); + /// @return \c true if the loaded ARM9 BIOS image is a known dump + /// of a native DS-compatible ARM9 BIOS. + [[nodiscard]] bool IsLoadedARM9BIOSKnownNative() const noexcept { return ARM9BIOSNative; } + [[nodiscard]] const std::array& GetARM9BIOS() const noexcept { return ARM9BIOS; } + void SetARM9BIOS(const std::array& bios) noexcept; -bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); -void LoadGBAAddon(int type); -void EjectGBACart(); + [[nodiscard]] const std::array& GetARM7BIOS() const noexcept { return ARM7BIOS; } + void SetARM7BIOS(const std::array& bios) noexcept; -u32 RunFrame(); + /// @return \c true if the loaded ARM7 BIOS image is a known dump + /// of a native DS-compatible ARM9 BIOS. + [[nodiscard]] bool IsLoadedARM7BIOSKnownNative() const noexcept { return ARM7BIOSNative; } -void TouchScreen(u16 x, u16 y); -void ReleaseScreen(); + [[nodiscard]] NDSCart::CartCommon* GetNDSCart() { return NDSCartSlot.GetCart(); } + [[nodiscard]] const NDSCart::CartCommon* GetNDSCart() const { return NDSCartSlot.GetCart(); } + virtual void SetNDSCart(std::unique_ptr&& cart); + [[nodiscard]] bool CartInserted() const noexcept { return NDSCartSlot.GetCart() != nullptr; } + virtual std::unique_ptr EjectCart() { return NDSCartSlot.EjectCart(); } -void SetKeyMask(u32 mask); + [[nodiscard]] u8* GetNDSSave() { return NDSCartSlot.GetSaveMemory(); } + [[nodiscard]] const u8* GetNDSSave() const { return NDSCartSlot.GetSaveMemory(); } + [[nodiscard]] u32 GetNDSSaveLength() const { return NDSCartSlot.GetSaveMemoryLength(); } + void SetNDSSave(const u8* savedata, u32 savelen); -bool IsLidClosed(); -void SetLidClosed(bool closed); + const Firmware& GetFirmware() const { return SPI.GetFirmwareMem()->GetFirmware(); } + Firmware& GetFirmware() { return SPI.GetFirmwareMem()->GetFirmware(); } + void SetFirmware(Firmware&& firmware) { SPI.GetFirmwareMem()->SetFirmware(std::move(firmware)); } -void CamInputFrame(int cam, u32* data, int width, int height, bool rgb); -void MicInputFrame(s16* data, int samples); + const Renderer3D& GetRenderer3D() const noexcept { return GPU.GetRenderer3D(); } + Renderer3D& GetRenderer3D() noexcept { return GPU.GetRenderer3D(); } + void SetRenderer3D(std::unique_ptr&& renderer) noexcept + { + if (renderer != nullptr) + GPU.SetRenderer3D(std::move(renderer)); + } -void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param); -void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param); -void CancelEvent(u32 id); + virtual bool NeedsDirectBoot() const; + void SetupDirectBoot(const std::string& romname); + virtual void SetupDirectBoot(); -void debug(u32 p); + [[nodiscard]] GBACart::CartCommon* GetGBACart() { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); } + [[nodiscard]] const GBACart::CartCommon* GetGBACart() const { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); } -void Halt(); + /// Inserts a GBA cart into the emulated console's Slot-2. + /// + /// @param cart The GBA cart, most likely (but not necessarily) returned from GBACart::ParseROM. + /// To insert an accessory that doesn't use a ROM image + /// (e.g. the Expansion Pak), create it manually and pass it here. + /// If \c nullptr, the existing cart is ejected. + /// If this is a DSi, this method does nothing. + /// + /// @post \c cart is \c nullptr and this NDS takes ownership + /// of the cart object it held, if any. + void SetGBACart(std::unique_ptr&& cart) { if (ConsoleType == 0) GBACartSlot.SetCart(std::move(cart)); } -void MapSharedWRAM(u8 val); + u8* GetGBASave() { return GBACartSlot.GetSaveMemory(); } + const u8* GetGBASave() const { return GBACartSlot.GetSaveMemory(); } + u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); } + void SetGBASave(const u8* savedata, u32 savelen); -void UpdateIRQ(u32 cpu); -void SetIRQ(u32 cpu, u32 irq); -void ClearIRQ(u32 cpu, u32 irq); -void SetIRQ2(u32 irq); -void ClearIRQ2(u32 irq); -bool HaltInterrupted(u32 cpu); -void StopCPU(u32 cpu, u32 mask); -void ResumeCPU(u32 cpu, u32 mask); -void GXFIFOStall(); -void GXFIFOUnstall(); + void LoadGBAAddon(int type); + std::unique_ptr EjectGBACart() { return GBACartSlot.EjectCart(); } -u32 GetPC(u32 cpu); -u64 GetSysClockCycles(int num); -void NocashPrint(u32 cpu, u32 addr); + u32 RunFrame(); -void MonitorARM9Jump(u32 addr); + bool IsRunning() const noexcept { return Running; } -bool DMAsInMode(u32 cpu, u32 mode); -bool DMAsRunning(u32 cpu); -void CheckDMAs(u32 cpu, u32 mode); -void StopDMAs(u32 cpu, u32 mode); + void TouchScreen(u16 x, u16 y); + void ReleaseScreen(); -void RunTimers(u32 cpu); + void SetKeyMask(u32 mask); -u8 ARM9Read8(u32 addr); -u16 ARM9Read16(u32 addr); -u32 ARM9Read32(u32 addr); -void ARM9Write8(u32 addr, u8 val); -void ARM9Write16(u32 addr, u16 val); -void ARM9Write32(u32 addr, u32 val); + bool IsLidClosed() const; + void SetLidClosed(bool closed); -bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region); + virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {} + void MicInputFrame(s16* data, int samples); -u8 ARM7Read8(u32 addr); -u16 ARM7Read16(u32 addr); -u32 ARM7Read32(u32 addr); -void ARM7Write8(u32 addr, u8 val); -void ARM7Write16(u32 addr, u16 val); -void ARM7Write32(u32 addr, u32 val); + void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); + void UnregisterEventFunc(u32 id, u32 funcid); + void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param); + void CancelEvent(u32 id); -bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region); + void debug(u32 p); -u8 ARM9IORead8(u32 addr); -u16 ARM9IORead16(u32 addr); -u32 ARM9IORead32(u32 addr); -void ARM9IOWrite8(u32 addr, u8 val); -void ARM9IOWrite16(u32 addr, u16 val); -void ARM9IOWrite32(u32 addr, u32 val); + void Halt(); -u8 ARM7IORead8(u32 addr); -u16 ARM7IORead16(u32 addr); -u32 ARM7IORead32(u32 addr); -void ARM7IOWrite8(u32 addr, u8 val); -void ARM7IOWrite16(u32 addr, u16 val); -void ARM7IOWrite32(u32 addr, u32 val); + void MapSharedWRAM(u8 val); + + void UpdateIRQ(u32 cpu); + void SetIRQ(u32 cpu, u32 irq); + void ClearIRQ(u32 cpu, u32 irq); + void SetIRQ2(u32 irq); + void ClearIRQ2(u32 irq); + bool HaltInterrupted(u32 cpu) const; + void StopCPU(u32 cpu, u32 mask); + void ResumeCPU(u32 cpu, u32 mask); + void GXFIFOStall(); + void GXFIFOUnstall(); + + u32 GetPC(u32 cpu) const; + u64 GetSysClockCycles(int num); + void NocashPrint(u32 cpu, u32 addr); + + void MonitorARM9Jump(u32 addr); + + virtual bool DMAsInMode(u32 cpu, u32 mode) const; + virtual bool DMAsRunning(u32 cpu) const; + virtual void CheckDMAs(u32 cpu, u32 mode); + virtual void StopDMAs(u32 cpu, u32 mode); + + void RunTimers(u32 cpu); + + virtual u8 ARM9Read8(u32 addr); + virtual u16 ARM9Read16(u32 addr); + virtual u32 ARM9Read32(u32 addr); + virtual void ARM9Write8(u32 addr, u8 val); + virtual void ARM9Write16(u32 addr, u16 val); + virtual void ARM9Write32(u32 addr, u32 val); + + virtual bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region); + + virtual u8 ARM7Read8(u32 addr); + virtual u16 ARM7Read16(u32 addr); + virtual u32 ARM7Read32(u32 addr); + virtual void ARM7Write8(u32 addr, u8 val); + virtual void ARM7Write16(u32 addr, u16 val); + virtual void ARM7Write32(u32 addr, u32 val); + + virtual bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region); + + virtual u8 ARM9IORead8(u32 addr); + virtual u16 ARM9IORead16(u32 addr); + virtual u32 ARM9IORead32(u32 addr); + virtual void ARM9IOWrite8(u32 addr, u8 val); + virtual void ARM9IOWrite16(u32 addr, u16 val); + virtual void ARM9IOWrite32(u32 addr, u32 val); + + virtual u8 ARM7IORead8(u32 addr); + virtual u16 ARM7IORead16(u32 addr); + virtual u32 ARM7IORead32(u32 addr); + virtual void ARM7IOWrite8(u32 addr, u8 val); + virtual void ARM7IOWrite16(u32 addr, u16 val); + virtual void ARM7IOWrite32(u32 addr, u32 val); + +#ifdef JIT_ENABLED + [[nodiscard]] bool IsJITEnabled() const noexcept { return EnableJIT; } + void SetJITArgs(std::optional args) noexcept; +#else + [[nodiscard]] bool IsJITEnabled() const noexcept { return false; } + void SetJITArgs(std::optional args) noexcept {} +#endif + +private: + void InitTimings(); + u32 SchedListMask; + u64 SysTimestamp; + u8 WRAMCnt; + u8 PostFlag9; + u8 PostFlag7; + u16 PowerControl7; + u16 WifiWaitCnt; + u8 TimerCheckMask[2]; + u64 TimerTimestamp[2]; + DMA DMAs[8]; + u32 DMA9Fill[4]; + u16 IPCSync9, IPCSync7; + u16 IPCFIFOCnt9, IPCFIFOCnt7; + FIFO IPCFIFO9; // FIFO in which the ARM9 writes + FIFO IPCFIFO7; + u16 DivCnt; + alignas(u64) u32 DivNumerator[2]; + alignas(u64) u32 DivDenominator[2]; + alignas(u64) u32 DivQuotient[2]; + alignas(u64) u32 DivRemainder[2]; + u16 SqrtCnt; + alignas(u64) u32 SqrtVal[2]; + u32 SqrtRes; + u16 KeyCnt[2]; + bool Running; + bool RunningGame; + u64 LastSysClockCycles; + u64 FrameStartTimestamp; + u64 NextTarget(); + u64 NextTargetSleep(); + void CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey); + void Reschedule(u64 target); + void RunSystemSleep(u64 timestamp); + void RunSystem(u64 timestamp); + void HandleTimerOverflow(u32 tid); + u16 TimerGetCounter(u32 timer); + void TimerStart(u32 id, u16 cnt); + void StartDiv(); + void DivDone(u32 param); + void SqrtDone(u32 param); + void StartSqrt(); + void RunTimer(u32 tid, s32 cycles); + void UpdateWifiTimings(); + void SetWifiWaitCnt(u16 val); + void SetGBASlotTimings(); + void EnterSleepMode(); + template + u32 RunFrame(); +public: + NDS(NDSArgs&& args) noexcept : NDS(std::move(args), 0) {} + NDS() noexcept; + virtual ~NDS() noexcept; + NDS(const NDS&) = delete; + NDS& operator=(const NDS&) = delete; + NDS(NDS&&) = delete; + NDS& operator=(NDS&&) = delete; + // The frontend should set and unset this manually after creating and destroying the NDS object. + [[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current; +protected: + explicit NDS(NDSArgs&& args, int type) noexcept; + virtual void DoSavestateExtra(Savestate* file) {} +}; } - #endif // NDS_H diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index f1598eb2..a64d8a27 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,52 +20,35 @@ #include "NDS.h" #include "DSi.h" #include "NDSCart.h" -#include "ARM.h" #include "CRC32.h" -#include "DSi_AES.h" #include "Platform.h" #include "ROMList.h" #include "melonDLDI.h" -#include "xxhash/xxhash.h" +#include "FATStorage.h" +#include "Utils.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; namespace NDSCart { +enum +{ + ROMTransfer_PrepareData = 0, + ROMTransfer_End +}; + // SRAM TODO: emulate write delays??? -u16 SPICnt; -u32 ROMCnt; - -u8 SPIData; -u32 SPIDataPos; -bool SPIHold; - -u8 ROMCommand[8]; -u32 ROMData; - -u8 TransferData[0x4000]; -u32 TransferPos; -u32 TransferLen; -u32 TransferDir; -u8 TransferCmd[8]; - -std::unique_ptr Cart; - -u32 Key1_KeyBuf[0x412]; - -u64 Key2_X; -u64 Key2_Y; - - -u32 ByteSwap(u32 val) +constexpr u32 ByteSwap(u32 val) { return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); } -void Key1_Encrypt(u32* data) +void NDSCartSlot::Key1_Encrypt(u32* data) const noexcept { u32 y = data[0]; u32 x = data[1]; @@ -86,7 +69,7 @@ void Key1_Encrypt(u32* data) data[1] = y ^ Key1_KeyBuf[0x11]; } -void Key1_Decrypt(u32* data) +void NDSCartSlot::Key1_Decrypt(u32* data) const noexcept { u32 y = data[0]; u32 x = data[1]; @@ -107,7 +90,7 @@ void Key1_Decrypt(u32* data) data[1] = y ^ Key1_KeyBuf[0x0]; } -void Key1_ApplyKeycode(u32* keycode, u32 mod) +void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept { Key1_Encrypt(&keycode[1]); Key1_Encrypt(&keycode[0]); @@ -126,9 +109,9 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod) } } -void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) +void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept { - if (externalBios) + if (NDS.IsLoadedARM7BIOSKnownNative()) { u32 expected_bios_length = dsi ? 0x10000 : 0x4000; if (biosLength != expected_bios_length) @@ -141,21 +124,21 @@ void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) } else { - memcpy(Key1_KeyBuf, bios + (dsi ? 0xC6D0 : 0x0030), 0x1048); + memcpy(Key1_KeyBuf.data(), bios + (dsi ? 0xC6D0 : 0x0030), sizeof(Key1_KeyBuf)); Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n"); } } else { // well - memset(Key1_KeyBuf, 0, 0x1048); + memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf)); Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n"); } } -void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) +void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept { - Key1_LoadKeyBuf(dsi, Platform::GetConfigBool(Platform::ExternalBIOSEnable), bios, biosLength); + Key1_LoadKeyBuf(dsi, bios, biosLength); u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; if (level >= 1) Key1_ApplyKeycode(keycode, mod); @@ -169,7 +152,7 @@ void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 bi } -void Key2_Encrypt(u8* data, u32 len) +void NDSCartSlot::Key2_Encrypt(const u8* data, u32 len) noexcept { for (u32 i = 0; i < len; i++) { @@ -190,27 +173,29 @@ void Key2_Encrypt(u8* data, u32 len) } -CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) +CartCommon::CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) : + CartCommon(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, type) { - ROM = rom; - ROMLength = len; - ChipID = chipid; - ROMParams = romparams; +} - memcpy(&Header, rom, sizeof(Header)); +CartCommon::CartCommon(std::unique_ptr&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) : + ROM(std::move(rom)), + ROMLength(len), + ChipID(chipid), + ROMParams(romparams), + CartType(type) +{ + memcpy(&Header, ROM.get(), sizeof(Header)); IsDSi = Header.IsDSi() && !badDSiDump; DSiBase = Header.DSiRegionStart << 19; } -CartCommon::~CartCommon() -{ - delete[] ROM; -} +CartCommon::~CartCommon() = default; u32 CartCommon::Checksum() const { const NDSHeader& header = GetHeader(); - u32 crc = CRC32(ROM, 0x40); + u32 crc = CRC32(ROM.get(), 0x40); crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc); crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc); @@ -231,11 +216,11 @@ void CartCommon::Reset() DSiMode = false; } -void CartCommon::SetupDirectBoot(const std::string& romname) +void CartCommon::SetupDirectBoot(const std::string& romname, NDS& nds) { CmdEncMode = 2; DataEncMode = 2; - DSiMode = IsDSi && (NDS::ConsoleType==1); + DSiMode = IsDSi && (nds.ConsoleType==1); } void CartCommon::DoSavestate(Savestate* file) @@ -247,15 +232,7 @@ void CartCommon::DoSavestate(Savestate* file) file->Bool32(&DSiMode); } -void CartCommon::SetupSave(u32 type) -{ -} - -void CartCommon::LoadSave(const u8* savedata, u32 savelen) -{ -} - -int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { if (CmdEncMode == 0) { @@ -284,15 +261,16 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) case 0x3C: CmdEncMode = 1; - Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.GetARM7BIOS().data(), ARM7BIOSSize); DSiMode = false; return 0; case 0x3D: if (IsDSi) { + auto& dsi = static_cast(nds); CmdEncMode = 1; - Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); + cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS)); DSiMode = true; } return 0; @@ -309,7 +287,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) u8 cmddec[8]; *(u32*)&cmddec[0] = ByteSwap(*(u32*)&cmd[4]); *(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmd[0]); - Key1_Decrypt((u32*)cmddec); + cartslot.Key1_Decrypt((u32*)cmddec); u32 tmp = ByteSwap(*(u32*)&cmddec[4]); *(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmddec[0]); *(u32*)&cmddec[0] = tmp; @@ -367,7 +345,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) return 0; } -void CartCommon::ROMCommandFinish(u8* cmd, u8* data, u32 len) +void CartCommon::ROMCommandFinish(const u8* cmd, u8* data, u32 len) { } @@ -376,29 +354,13 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last) return 0xFF; } -void CartCommon::SetIRQ() -{ - NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); - NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); -} - -u8 *CartCommon::GetSaveMemory() const -{ - return nullptr; -} - -u32 CartCommon::GetSaveMemoryLength() const -{ - return 0; -} - -void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) +void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) const { if (addr >= ROMLength) return; if ((addr+len) > ROMLength) len = ROMLength - addr; - memcpy(data+offset, ROM+addr, len); + memcpy(data+offset, ROM.get()+addr, len); } const NDSBanner* CartCommon::Banner() const @@ -407,22 +369,68 @@ const NDSBanner* CartCommon::Banner() const size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40; if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize)) { - return reinterpret_cast(ROM + header.BannerOffset); + return reinterpret_cast(ROM.get() + header.BannerOffset); } return nullptr; } -CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) : CartCommon(rom, len, chipid, badDSiDump, romparams) +CartRetail::CartRetail(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen, melonDS::NDSCart::CartType type) : + CartRetail(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, type) { - SRAM = nullptr; } -CartRetail::~CartRetail() +CartRetail::CartRetail(std::unique_ptr&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen, melonDS::NDSCart::CartType type) : + CartCommon(std::move(rom), len, chipid, badDSiDump, romparams, type) { - if (SRAM) delete[] SRAM; + u32 savememtype = ROMParams.SaveMemType <= 10 ? ROMParams.SaveMemType : 0; + constexpr int sramlengths[] = + { + 0, + 512, + 8192, 65536, 128*1024, + 256*1024, 512*1024, 1024*1024, + 8192*1024, 16384*1024, 65536*1024 + }; + SRAMLength = sramlengths[savememtype]; + + if (SRAMLength) + { // If this cart should have any save data... + if (sram && sramlen == SRAMLength) + { // If we were given save data that already has the correct length... + SRAM = std::move(sram); + } + else + { // Copy in what we can, truncate the rest. + SRAM = std::make_unique(SRAMLength); + memset(SRAM.get(), 0xFF, SRAMLength); + + if (sram) + { // If we have anything to copy, that is. + memcpy(SRAM.get(), sram.get(), std::min(sramlen, SRAMLength)); + } + } + } + + switch (savememtype) + { + case 1: SRAMType = 1; break; // EEPROM, small + case 2: + case 3: + case 4: SRAMType = 2; break; // EEPROM, regular + case 5: + case 6: + case 7: SRAMType = 3; break; // FLASH + case 8: + case 9: + case 10: SRAMType = 4; break; // NAND + default: SRAMType = 0; break; // ...whatever else + } } +CartRetail::~CartRetail() = default; +// std::unique_ptr cleans up the SRAM and ROM + void CartRetail::Reset() { CartCommon::Reset(); @@ -447,13 +455,11 @@ void CartRetail::DoSavestate(Savestate* file) Log(LogLevel::Warn, "savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); Log(LogLevel::Warn, "oh well. loading it anyway. adsfgdsf\n"); - if (oldlen) delete[] SRAM; - SRAM = nullptr; - if (SRAMLength) SRAM = new u8[SRAMLength]; + SRAM = SRAMLength ? std::make_unique(SRAMLength) : nullptr; } if (SRAMLength) { - file->VarArray(SRAM, SRAMLength); + file->VarArray(SRAM.get(), SRAMLength); } // SPI status shito @@ -463,59 +469,21 @@ void CartRetail::DoSavestate(Savestate* file) file->Var8(&SRAMStatus); if ((!file->Saving) && SRAM) - Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength); + Platform::WriteNDSSave(SRAM.get(), SRAMLength, 0, SRAMLength); } -void CartRetail::SetupSave(u32 type) -{ - if (SRAM) delete[] SRAM; - SRAM = nullptr; - - if (type > 10) type = 0; - int sramlen[] = - { - 0, - 512, - 8192, 65536, 128*1024, - 256*1024, 512*1024, 1024*1024, - 8192*1024, 16384*1024, 65536*1024 - }; - SRAMLength = sramlen[type]; - - if (SRAMLength) - { - SRAM = new u8[SRAMLength]; - memset(SRAM, 0xFF, SRAMLength); - } - - switch (type) - { - case 1: SRAMType = 1; break; // EEPROM, small - case 2: - case 3: - case 4: SRAMType = 2; break; // EEPROM, regular - case 5: - case 6: - case 7: SRAMType = 3; break; // FLASH - case 8: - case 9: - case 10: SRAMType = 4; break; // NAND - default: SRAMType = 0; break; // ...whatever else - } -} - -void CartRetail::LoadSave(const u8* savedata, u32 savelen) +void CartRetail::SetSaveMemory(const u8* savedata, u32 savelen) { if (!SRAM) return; u32 len = std::min(savelen, SRAMLength); - memcpy(SRAM, savedata, len); + memcpy(SRAM.get(), savedata, len); Platform::WriteNDSSave(savedata, len, 0, len); } -int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartRetail::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); switch (cmd[0]) { @@ -536,7 +504,7 @@ int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) return 0; default: - return CartCommon::ROMCommandStart(cmd, data, len); + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); } } @@ -573,17 +541,7 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last) } } -u8 *CartRetail::GetSaveMemory() const -{ - return SRAM; -} - -u32 CartRetail::GetSaveMemoryLength() const -{ - return SRAMLength; -} - -void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) +void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const { addr &= (ROMLength-1); @@ -600,7 +558,7 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) addr = 0x8000 + (addr & 0x1FF); } - memcpy(data+offset, ROM+addr, len); + memcpy(data+offset, ROM.get()+addr, len); } u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) @@ -635,7 +593,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, (SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr); } return 0; @@ -699,7 +657,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -756,7 +714,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -793,7 +751,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -839,7 +797,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -862,7 +820,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -874,15 +832,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) } } - -CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams) +CartRetailNAND::CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetailNAND(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen) { } -CartRetailNAND::~CartRetailNAND() +CartRetailNAND::CartRetailNAND(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailNAND) { + BuildSRAMID(); } +CartRetailNAND::~CartRetailNAND() = default; + void CartRetailNAND::Reset() { CartRetail::Reset(); @@ -911,15 +873,15 @@ void CartRetailNAND::DoSavestate(Savestate* file) BuildSRAMID(); } -void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen) +void CartRetailNAND::SetSaveMemory(const u8* savedata, u32 savelen) { - CartRetail::LoadSave(savedata, savelen); + CartRetail::SetSaveMemory(savedata, savelen); BuildSRAMID(); } -int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); switch (cmd[0]) { @@ -946,7 +908,7 @@ int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000)) { memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); - Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800); + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMAddr - SRAMBase, 0x800); } SRAMAddr = 0; @@ -1049,11 +1011,11 @@ int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) return 0; default: - return CartRetail::ROMCommandStart(cmd, data, len); + return CartRetail::ROMCommandStart(nds, cartslot, cmd, data, len); } } -void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len) +void CartRetailNAND::ROMCommandFinish(const u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); @@ -1102,15 +1064,28 @@ void CartRetailNAND::BuildSRAMID() } -CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams) : CartRetail(rom, len, chipid, badDSiDump, romparams) +CartRetailIR::CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetailIR(CopyToUnique(rom, len), len, chipid, irversion, badDSiDump, romparams, std::move(sram), sramlen) { - IRVersion = irversion; } -CartRetailIR::~CartRetailIR() +CartRetailIR::CartRetailIR( + std::unique_ptr&& rom, + u32 len, + u32 chipid, + u32 irversion, + bool badDSiDump, + ROMListEntry romparams, + std::unique_ptr&& sram, + u32 sramlen +) : + CartRetail(std::move(rom), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, CartType::RetailIR), + IRVersion(irversion) { } +CartRetailIR::~CartRetailIR() = default; + void CartRetailIR::Reset() { CartRetail::Reset(); @@ -1147,29 +1122,22 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last) return 0; } +CartRetailBT::CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetailBT(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen) +{ +} -CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams) +CartRetailBT::CartRetailBT(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailBT) { Log(LogLevel::Info,"POKETYPE CART\n"); } -CartRetailBT::~CartRetailBT() -{ -} - -void CartRetailBT::Reset() -{ - CartRetail::Reset(); -} - -void CartRetailBT::DoSavestate(Savestate* file) -{ - CartRetail::DoSavestate(file); -} +CartRetailBT::~CartRetailBT() = default; u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) { - Log(LogLevel::Debug,"POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0)); + //Log(LogLevel::Debug,"POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0)); /*if (pos == 0) { @@ -1182,149 +1150,23 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) } -CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams) +CartSD::CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartSD(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard)) +{} + +CartSD::CartSD(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartCommon(std::move(rom), len, chipid, false, romparams, CartType::Homebrew), + SD(std::move(sdcard)) { - SD = nullptr; + sdcard = std::nullopt; + // std::move on optionals usually results in an optional with a moved-from object } -CartHomebrew::~CartHomebrew() -{ - if (SD) - { - SD->Close(); - delete SD; - } -} +CartSD::~CartSD() = default; +// The SD card is destroyed by the optional's destructor -void CartHomebrew::Reset() -{ - CartCommon::Reset(); - ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly); - - if (SD) - { - SD->Close(); - delete SD; - } - - if (Platform::GetConfigBool(Platform::DLDI_Enable)) - { - std::string folderpath; - if (Platform::GetConfigBool(Platform::DLDI_FolderSync)) - folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath); - else - folderpath = ""; - - ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly); - SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath), - (u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024, - ReadOnly, - folderpath); - SD->Open(); - } - else - SD = nullptr; -} - -void CartHomebrew::SetupDirectBoot(const std::string& romname) -{ - CartCommon::SetupDirectBoot(romname); - - if (SD) - { - // add the ROM to the SD volume - - if (!SD->InjectFile(romname, ROM, ROMLength)) - return; - - // setup argv command line - - char argv[512] = {0}; - int argvlen; - - strncpy(argv, "fat:/", 511); - strncat(argv, romname.c_str(), 511); - argvlen = strlen(argv); - - const NDSHeader& header = GetHeader(); - void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32; - - u32 argvbase = header.ARM9RAMAddress + header.ARM9Size; - argvbase = (argvbase + 0xF) & ~0xF; - - for (u32 i = 0; i <= argvlen; i+=4) - writefn(argvbase+i, *(u32*)&argv[i]); - - writefn(0x02FFFE70, 0x5F617267); - writefn(0x02FFFE74, argvbase); - writefn(0x02FFFE78, argvlen+1); - } -} - -void CartHomebrew::DoSavestate(Savestate* file) -{ - CartCommon::DoSavestate(file); -} - -int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len) -{ - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); - - switch (cmd[0]) - { - case 0xB7: - { - u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - memset(data, 0, len); - - if (((addr + len - 1) >> 12) != (addr >> 12)) - { - u32 len1 = 0x1000 - (addr & 0xFFF); - ReadROM_B7(addr, len1, data, 0); - ReadROM_B7(addr+len1, len-len1, data, len1); - } - else - ReadROM_B7(addr, len, data, 0); - } - return 0; - - case 0xC0: // SD read - { - u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - if (SD) SD->ReadSectors(sector, len>>9, data); - } - return 0; - - case 0xC1: // SD write - return 1; - - default: - return CartCommon::ROMCommandStart(cmd, data, len); - } -} - -void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len) -{ - if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); - - // TODO: delayed SD writing? like we have for SRAM - - switch (cmd[0]) - { - case 0xC1: - { - u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data); - } - break; - - default: - return CartCommon::ROMCommandFinish(cmd, data, len); - } -} - -void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) +void CartSD::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const { if (patch[0x0D] > binary[dldioffset+0x0F]) { @@ -1425,7 +1267,7 @@ void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, Log(LogLevel::Debug, "applied DLDI patch at %08X\n", dldioffset); } -void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) +void CartSD::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) { if (*(u32*)&patch[0] != 0xBF8DA5ED || *(u32*)&patch[4] != 0x69684320 || @@ -1455,35 +1297,151 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) } } -void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) +void CartSD::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const { // TODO: how strict should this be for homebrew? addr &= (ROMLength-1); - memcpy(data+offset, ROM+addr, len); + memcpy(data+offset, ROM.get()+addr, len); } +CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartSD(rom, len, chipid, romparams, std::move(sdcard)) +{} +CartHomebrew::CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard)) +{} -bool Init() +CartHomebrew::~CartHomebrew() = default; + +void CartHomebrew::Reset() { - Cart = nullptr; + CartSD::Reset(); - return true; + if (SD) + ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly()); } -void DeInit() +void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) { - Cart = nullptr; + CartCommon::SetupDirectBoot(romname, nds); + + if (SD) + { + // add the ROM to the SD volume + + if (!SD->InjectFile(romname, ROM.get(), ROMLength)) + return; + + // setup argv command line + + char argv[512] = {0}; + int argvlen; + + strncpy(argv, "fat:/", 511); + strncat(argv, romname.c_str(), 511); + argvlen = strlen(argv); + + const NDSHeader& header = GetHeader(); + + u32 argvbase = header.ARM9RAMAddress + header.ARM9Size; + argvbase = (argvbase + 0xF) & ~0xF; + + for (u32 i = 0; i <= argvlen; i+=4) + nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]); + + nds.ARM9Write32(0x02FFFE70, 0x5F617267); + nds.ARM9Write32(0x02FFFE74, argvbase); + nds.ARM9Write32(0x02FFFE78, argvlen+1); + // The DSi version of ARM9Write32 will be called if nds is really a DSi + } } -void Reset() +int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); + + switch (cmd[0]) + { + case 0xB7: + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memset(data, 0, len); + + if (((addr + len - 1) >> 12) != (addr >> 12)) + { + u32 len1 = 0x1000 - (addr & 0xFFF); + ReadROM_B7(addr, len1, data, 0); + ReadROM_B7(addr+len1, len-len1, data, len1); + } + else + ReadROM_B7(addr, len, data, 0); + } + return 0; + + case 0xC0: // SD read + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (SD) SD->ReadSectors(sector, len>>9, data); + } + return 0; + + case 0xC1: // SD write + return 1; + + default: + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); + } +} + +void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); + + // TODO: delayed SD writing? like we have for SRAM + + switch (cmd[0]) + { + case 0xC1: + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data); + } + break; + + default: + return CartCommon::ROMCommandFinish(cmd, data, len); + } +} + +NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) noexcept : NDS(nds) +{ + NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); + NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); + NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); + // All fields are default-constructed because they're listed as such in the class declaration + + if (rom) + SetCart(std::move(rom)); +} + +NDSCartSlot::~NDSCartSlot() noexcept +{ + NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData); + NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_End); + NDS.UnregisterEventFunc(Event_ROMSPITransfer, 0); + + // Cart is cleaned up automatically because it's a unique_ptr +} + +void NDSCartSlot::Reset() noexcept { ResetCart(); } -void DoSavestate(Savestate* file) +void NDSCartSlot::DoSavestate(Savestate* file) noexcept { file->Section("NDSC"); @@ -1494,14 +1452,14 @@ void DoSavestate(Savestate* file) file->Var32(&SPIDataPos); file->Bool32(&SPIHold); - file->VarArray(ROMCommand, 8); + file->VarArray(ROMCommand.data(), sizeof(ROMCommand)); file->Var32(&ROMData); - file->VarArray(TransferData, 0x4000); + file->VarArray(TransferData.data(), sizeof(TransferData)); file->Var32(&TransferPos); file->Var32(&TransferLen); file->Var32(&TransferDir); - file->VarArray(TransferCmd, 8); + file->VarArray(TransferCmd.data(), sizeof(TransferCmd)); // cart inserted/len/ROM/etc should be already populated // savestate should be loaded after the right game is loaded @@ -1576,7 +1534,7 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params) } -void DecryptSecureArea(u8* out) +void NDSCartSlot::DecryptSecureArea(u8* out) noexcept { const NDSHeader& header = Cart->GetHeader(); const u8* cartrom = Cart->GetROM(); @@ -1586,16 +1544,13 @@ void DecryptSecureArea(u8* out) memcpy(out, &cartrom[arm9base], 0x800); - Key1_InitKeycode(false, gamecode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_Decrypt((u32*)&out[0]); - Key1_InitKeycode(false, gamecode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); for (u32 i = 0; i < 0x800; i += 8) Key1_Decrypt((u32*)&out[i]); - XXH64_hash_t hash = XXH64(out, 0x800, 0); - Log(LogLevel::Debug, "Secure area post-decryption xxh64 hash: %zx\n", hash); - if (!strncmp((const char*)out, "encryObj", 8)) { Log(LogLevel::Info, "Secure area decryption OK\n"); @@ -1610,7 +1565,12 @@ void DecryptSecureArea(u8* out) } } -std::unique_ptr ParseROM(const u8* romdata, u32 romlen) +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, std::optional&& args) +{ + return ParseROM(CopyToUnique(romdata, romlen), romlen, std::move(args)); +} + +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::optional&& args) { if (romdata == nullptr) { @@ -1624,40 +1584,22 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) return nullptr; } - u32 cartromsize = 0x200; - while (cartromsize < romlen) - cartromsize <<= 1; // ROM size must be a power of 2 - - u8* cartrom = nullptr; - try - { - cartrom = new u8[cartromsize]; - } - catch (const std::bad_alloc& e) - { - Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", cartromsize); - - return nullptr; - } - - // copy romdata into cartrom then zero out the remaining space - memcpy(cartrom, romdata, romlen); - memset(cartrom + romlen, 0, cartromsize - romlen); + auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen); NDSHeader header {}; - memcpy(&header, cartrom, sizeof(header)); + memcpy(&header, cartrom.get(), sizeof(header)); bool dsi = header.IsDSi(); bool badDSiDump = false; - u32 dsiRegion = header.DSiRegionMask; - if (dsi && dsiRegion == 0) + if (dsi && header.DSiRegionMask == RegionMask::NoRegion) { Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n"); badDSiDump = true; dsi = false; } + const char *gametitle = header.GameTitle; u32 gamecode = header.GameCodeAsU32(); u32 arm9base = header.ARM9ROMOffset; @@ -1712,37 +1654,44 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) } std::unique_ptr cart; + std::unique_ptr sram = args ? std::move(args->SRAM) : nullptr; + u32 sramlen = args ? args->SRAMLength : 0; if (homebrew) - cart = std::make_unique(cartrom, cartromsize, cartid, romparams); + { + std::optional sdcard = args && args->SDCard ? std::make_optional(std::move(*args->SDCard)) : std::nullopt; + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sdcard)); + } + else if (gametitle[0] == 0 && !strncmp("SD/TF-NDS", gametitle + 1, 9) && gamecode == 0x414D5341) + { + std::optional sdcard = args && args->SDCard ? std::make_optional(std::move(*args->SDCard)) : std::nullopt; + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, CartR4TypeR4, CartR4LanguageEnglish, std::move(sdcard)); + } else if (cartid & 0x08000000) - cart = std::make_unique(cartrom, cartromsize, cartid, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen); else if (irversion != 0) - cart = std::make_unique(cartrom, cartromsize, cartid, irversion, badDSiDump, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, irversion, badDSiDump, romparams, std::move(sram), sramlen); else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx - cart = std::make_unique(cartrom, cartromsize, cartid, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen); else - cart = std::make_unique(cartrom, cartromsize, cartid, badDSiDump, romparams); - - if (romparams.SaveMemType > 0) - cart->SetupSave(romparams.SaveMemType); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, badDSiDump, romparams, std::move(sram), sramlen); + args = std::nullopt; return cart; } -// Why a move function? Because the Cart object is polymorphic, -// and cloning polymorphic objects without knowing the underlying type is annoying. -bool InsertROM(std::unique_ptr&& cart) +void NDSCartSlot::SetCart(std::unique_ptr&& cart) noexcept { - if (!cart) { - Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n"); - return false; - } - if (Cart) EjectCart(); + // Why a move function? Because the Cart object is polymorphic, + // and cloning polymorphic objects without knowing the underlying type is annoying. Cart = std::move(cart); + if (!Cart) + // If we're ejecting an existing cart without inserting a new one... + return; + Cart->Reset(); const NDSHeader& header = Cart->GetHeader(); @@ -1757,11 +1706,11 @@ bool InsertROM(std::unique_ptr&& cart) strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8); - Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); for (u32 i = 0; i < 0x800; i += 8) Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]); - Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]); Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); @@ -1775,57 +1724,34 @@ bool InsertROM(std::unique_ptr&& cart) Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode); Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID()); Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); - - DSi::SetCartInserted(true); - - return true; } -bool LoadROM(const u8* romdata, u32 romlen) -{ - std::unique_ptr cart = ParseROM(romdata, romlen); - - return InsertROM(std::move(cart)); -} - -void LoadSave(const u8* savedata, u32 savelen) +void NDSCartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept { if (Cart) - Cart->LoadSave(savedata, savelen); + Cart->SetSaveMemory(savedata, savelen); } -void SetupDirectBoot(const std::string& romname) +void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept { if (Cart) - Cart->SetupDirectBoot(romname); + Cart->SetupDirectBoot(romname, NDS); } -u8* GetSaveMemory() +std::unique_ptr NDSCartSlot::EjectCart() noexcept { - return Cart ? Cart->GetSaveMemory() : nullptr; -} - -u32 GetSaveMemoryLength() -{ - return Cart ? Cart->GetSaveMemoryLength() : 0; -} - -void EjectCart() -{ - if (!Cart) return; + if (!Cart) return nullptr; // ejecting the cart triggers the gamecard IRQ - NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); - NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); + NDS.SetIRQ(0, IRQ_CartIREQMC); + NDS.SetIRQ(1, IRQ_CartIREQMC); - Cart = nullptr; - - DSi::SetCartInserted(false); + return std::move(Cart); // CHECKME: does an eject imply anything for the ROM/SPI transfer registers? } -void ResetCart() +void NDSCartSlot::ResetCart() noexcept { // CHECKME: what if there is a transfer in progress? @@ -1836,35 +1762,35 @@ void ResetCart() SPIDataPos = 0; SPIHold = false; - memset(ROMCommand, 0, 8); + memset(ROMCommand.data(), 0, sizeof(ROMCommand)); ROMData = 0; Key2_X = 0; Key2_Y = 0; - memset(TransferData, 0, 0x4000); + memset(TransferData.data(), 0, sizeof(TransferData)); TransferPos = 0; TransferLen = 0; TransferDir = 0; - memset(TransferCmd, 0, 8); + memset(TransferCmd.data(), 0, sizeof(TransferCmd)); TransferCmd[0] = 0xFF; if (Cart) Cart->Reset(); } -void ROMEndTransfer(u32 param) +void NDSCartSlot::ROMEndTransfer(u32 param) noexcept { ROMCnt &= ~(1<<31); if (SPICnt & (1<<14)) - NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartXferDone); + NDS.SetIRQ((NDS.ExMemCnt[0]>>11)&0x1, IRQ_CartXferDone); if (Cart) - Cart->ROMCommandFinish(TransferCmd, TransferData, TransferLen); + Cart->ROMCommandFinish(TransferCmd.data(), TransferData.data(), TransferLen); } -void ROMPrepareData(u32 param) +void NDSCartSlot::ROMPrepareData(u32 param) noexcept { if (TransferDir == 0) { @@ -1878,13 +1804,13 @@ void ROMPrepareData(u32 param) ROMCnt |= (1<<23); - if (NDS::ExMemCnt[0] & (1<<11)) - NDS::CheckDMAs(1, 0x12); + if (NDS.ExMemCnt[0] & (1<<11)) + NDS.CheckDMAs(1, 0x12); else - NDS::CheckDMAs(0, 0x05); + NDS.CheckDMAs(0, 0x05); } -void WriteROMCnt(u32 val) +void NDSCartSlot::WriteROMCnt(u32 val) noexcept { u32 xferstart = (val & ~ROMCnt) & (1<<31); ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x20800000); @@ -1893,9 +1819,9 @@ void WriteROMCnt(u32 val) // a DS cart reader if (val & (1<<15)) { - u32 snum = (NDS::ExMemCnt[0]>>8)&0x8; - u64 seed0 = *(u32*)&NDS::ROMSeed0[snum] | ((u64)NDS::ROMSeed0[snum+4] << 32); - u64 seed1 = *(u32*)&NDS::ROMSeed1[snum] | ((u64)NDS::ROMSeed1[snum+4] << 32); + u32 snum = (NDS.ExMemCnt[0]>>8)&0x8; + u64 seed0 = *(u32*)&NDS.ROMSeed0[snum] | ((u64)NDS.ROMSeed0[snum+4] << 32); + u64 seed1 = *(u32*)&NDS.ROMSeed1[snum] | ((u64)NDS.ROMSeed1[snum+4] << 32); Key2_X = 0; Key2_Y = 0; @@ -1929,7 +1855,7 @@ void WriteROMCnt(u32 val) *(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0]; *(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4]; - memset(TransferData, 0xFF, TransferLen); + memset(TransferData.data(), 0xFF, TransferLen); /*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n", SPICnt, ROMCnt, @@ -1942,7 +1868,7 @@ void WriteROMCnt(u32 val) TransferDir = 0; if (Cart) - TransferDir = Cart->ROMCommandStart(TransferCmd, TransferData, TransferLen); + TransferDir = Cart->ROMCommandStart(NDS, *this, TransferCmd.data(), TransferData.data(), TransferLen); if ((datasize > 0) && (((ROMCnt >> 30) & 0x1) != TransferDir)) Log(LogLevel::Debug, "NDSCART: !! BAD TRANSFER DIRECTION FOR CMD %02X, DIR=%d, ROMCNT=%08X\n", ROMCommand[0], TransferDir, ROMCnt); @@ -1968,12 +1894,12 @@ void WriteROMCnt(u32 val) } if (datasize == 0) - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*cmddelay, ROMEndTransfer, 0); + NDS.ScheduleEvent(Event_ROMTransfer, false, xfercycle*cmddelay, ROMTransfer_End, 0); else - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMPrepareData, 0); + NDS.ScheduleEvent(Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMTransfer_PrepareData, 0); } -void AdvanceROMTransfer() +void NDSCartSlot::AdvanceROMTransfer() noexcept { ROMCnt &= ~(1<<23); @@ -1987,13 +1913,13 @@ void AdvanceROMTransfer() delay += ((ROMCnt >> 16) & 0x3F); } - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*delay, ROMPrepareData, 0); + NDS.ScheduleEvent(Event_ROMTransfer, false, xfercycle*delay, ROMTransfer_PrepareData, 0); } else ROMEndTransfer(0); } -u32 ReadROMData() +u32 NDSCartSlot::ReadROMData() noexcept { if (ROMCnt & (1<<30)) return 0; @@ -2005,7 +1931,7 @@ u32 ReadROMData() return ROMData; } -void WriteROMData(u32 val) +void NDSCartSlot::WriteROMData(u32 val) noexcept { if (!(ROMCnt & (1<<30))) return; @@ -2026,7 +1952,7 @@ void WriteROMData(u32 val) } -void WriteSPICnt(u16 val) +void NDSCartSlot::WriteSPICnt(u16 val) noexcept { if ((SPICnt & 0x2040) == 0x2040 && (val & 0x2000) == 0x0000) { @@ -2043,12 +1969,12 @@ void WriteSPICnt(u16 val) Log(LogLevel::Debug, "!! CHANGING AUXSPICNT DURING TRANSFER: %04X\n", val); } -void SPITransferDone(u32 param) +void NDSCartSlot::SPITransferDone(u32 param) noexcept { SPICnt &= ~(1<<7); } -u8 ReadSPIData() +u8 NDSCartSlot::ReadSPIData() const noexcept { if (!(SPICnt & (1<<15))) return 0; if (!(SPICnt & (1<<13))) return 0; @@ -2057,7 +1983,7 @@ u8 ReadSPIData() return SPIData; } -void WriteSPIData(u8 val) +void NDSCartSlot::WriteSPIData(u8 val) noexcept { if (!(SPICnt & (1<<15))) return; if (!(SPICnt & (1<<13))) return; @@ -2089,7 +2015,9 @@ void WriteSPIData(u8 val) // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (SPICnt & 0x3)); - NDS::ScheduleEvent(NDS::Event_ROMSPITransfer, false, delay, SPITransferDone, 0); + NDS.ScheduleEvent(Event_ROMSPITransfer, false, delay, 0, 0); } } + +} \ No newline at end of file diff --git a/src/NDSCart.h b/src/NDSCart.h index f397225f..2f6a3be5 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,8 +19,10 @@ #ifndef NDSCART_H #define NDSCART_H +#include #include #include +#include #include "types.h" #include "Savestate.h" @@ -28,7 +30,11 @@ #include "FATStorage.h" #include "ROMList.h" -namespace NDSCart +namespace melonDS +{ +class NDS; +} +namespace melonDS::NDSCart { enum CartType @@ -39,33 +45,59 @@ enum CartType RetailIR = 0x103, RetailBT = 0x104, Homebrew = 0x201, + UnlicensedR4 = 0x301 +}; + +class NDSCartSlot; + +/// Arguments used to create and populate an NDS cart of unknown type. +/// Different carts take different subsets of these arguments, +/// but we won't know which ones to use +/// until we parse the header at runtime. +struct NDSCartArgs +{ + /// The arguments used to load a homebrew SD card image. + /// If \c nullopt, then the cart will not have an SD card. + /// Ignored for retail ROMs. + std::optional SDCard = std::nullopt; + + /// Save RAM to load into the cartridge. + /// If \c nullptr, then the cart's SRAM buffer will be empty. + /// Ignored for homebrew ROMs. + std::unique_ptr SRAM = nullptr; + + /// The length of the buffer in SRAM. + /// If 0, then the cart's SRAM buffer will be empty. + /// Ignored for homebrew ROMs. + u32 SRAMLength = 0; }; // CartCommon -- base code shared by all cart types class CartCommon { public: - CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams); + CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type); + CartCommon(std::unique_ptr&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type); virtual ~CartCommon(); - virtual u32 Type() const = 0; + [[nodiscard]] u32 Type() const { return CartType; }; [[nodiscard]] u32 Checksum() const; virtual void Reset(); - virtual void SetupDirectBoot(const std::string& romname); + virtual void SetupDirectBoot(const std::string& romname, NDS& nds); virtual void DoSavestate(Savestate* file); - virtual void SetupSave(u32 type); - virtual void LoadSave(const u8* savedata, u32 savelen); - virtual int ROMCommandStart(u8* cmd, u8* data, u32 len); - virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); + virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len); + virtual void ROMCommandFinish(const u8* cmd, u8* data, u32 len); virtual u8 SPIWrite(u8 val, u32 pos, bool last); - virtual u8* GetSaveMemory() const; - virtual u32 GetSaveMemoryLength() const; + virtual u8* GetSaveMemory() { return nullptr; } + virtual const u8* GetSaveMemory() const { return nullptr; } + virtual u32 GetSaveMemoryLength() const { return 0; } + virtual void SetSaveMemory(const u8* savedata, u32 savelen) {}; [[nodiscard]] const NDSHeader& GetHeader() const { return Header; } [[nodiscard]] NDSHeader& GetHeader() { return Header; } @@ -74,107 +106,120 @@ public: [[nodiscard]] const NDSBanner* Banner() const; [[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; }; [[nodiscard]] u32 ID() const { return ChipID; } - [[nodiscard]] const u8* GetROM() const { return ROM; } + [[nodiscard]] const u8* GetROM() const { return ROM.get(); } [[nodiscard]] u32 GetROMLength() const { return ROMLength; } protected: - void ReadROM(u32 addr, u32 len, u8* data, u32 offset); + void ReadROM(u32 addr, u32 len, u8* data, u32 offset) const; - void SetIRQ(); + std::unique_ptr ROM = nullptr; + u32 ROMLength = 0; + u32 ChipID = 0; + bool IsDSi = false; + bool DSiMode = false; + u32 DSiBase = 0; - u8* ROM; - u32 ROMLength; - u32 ChipID; - bool IsDSi; - bool DSiMode; - u32 DSiBase; - - u32 CmdEncMode; - u32 DataEncMode; + u32 CmdEncMode = 0; + u32 DataEncMode = 0; // Kept separate from the ROM data so we can decrypt the modcrypt area // without touching the overall ROM data - NDSHeader Header; - ROMListEntry ROMParams; + NDSHeader Header {}; + ROMListEntry ROMParams {}; + const melonDS::NDSCart::CartType CartType = Default; }; // CartRetail -- regular retail cart (ROM, SPI SRAM) class CartRetail : public CartCommon { public: - CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams); - virtual ~CartRetail() override; + CartRetail( + const u8* rom, + u32 len, + u32 chipid, + bool badDSiDump, + ROMListEntry romparams, + std::unique_ptr&& sram, + u32 sramlen, + melonDS::NDSCart::CartType type = CartType::Retail + ); + CartRetail( + std::unique_ptr&& rom, + u32 len, u32 chipid, + bool badDSiDump, + ROMListEntry romparams, + std::unique_ptr&& sram, + u32 sramlen, + melonDS::NDSCart::CartType type = CartType::Retail + ); + ~CartRetail() override; - virtual u32 Type() const override { return CartType::Retail; } + void Reset() override; - virtual void Reset() override; + void DoSavestate(Savestate* file) override; - virtual void DoSavestate(Savestate* file) override; + void SetSaveMemory(const u8* savedata, u32 savelen) override; - virtual void SetupSave(u32 type) override; - virtual void LoadSave(const u8* savedata, u32 savelen) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; - virtual int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + u8 SPIWrite(u8 val, u32 pos, bool last) override; - virtual u8 SPIWrite(u8 val, u32 pos, bool last) override; - - virtual u8* GetSaveMemory() const override; - virtual u32 GetSaveMemoryLength() const override; + u8* GetSaveMemory() override { return SRAM.get(); } + const u8* GetSaveMemory() const override { return SRAM.get(); } + u32 GetSaveMemoryLength() const override { return SRAMLength; } protected: - void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); + void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const; u8 SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last); u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last); u8 SRAMWrite_FLASH(u8 val, u32 pos, bool last); - u8* SRAM; - u32 SRAMLength; - u32 SRAMType; + std::unique_ptr SRAM = nullptr; + u32 SRAMLength = 0; + u32 SRAMType = 0; - u8 SRAMCmd; - u32 SRAMAddr; - u32 SRAMFirstAddr; - u8 SRAMStatus; + u8 SRAMCmd = 0; + u32 SRAMAddr = 0; + u32 SRAMFirstAddr = 0; + u8 SRAMStatus = 0; }; // CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...) class CartRetailNAND : public CartRetail { public: - CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); + CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); + CartRetailNAND(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); ~CartRetailNAND() override; - virtual u32 Type() const override { return CartType::RetailNAND; } - void Reset() override; void DoSavestate(Savestate* file) override; - void LoadSave(const u8* savedata, u32 savelen) override; + void SetSaveMemory(const u8* savedata, u32 savelen) override; - int ROMCommandStart(u8* cmd, u8* data, u32 len) override; - void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; u8 SPIWrite(u8 val, u32 pos, bool last) override; private: void BuildSRAMID(); - u32 SRAMBase; - u32 SRAMWindow; + u32 SRAMBase = 0; + u32 SRAMWindow = 0; - u8 SRAMWriteBuffer[0x800]; - u32 SRAMWritePos; + u8 SRAMWriteBuffer[0x800] {}; + u32 SRAMWritePos = 0; }; // CartRetailIR -- SPI IR device and SRAM class CartRetailIR : public CartRetail { public: - CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams); + CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); + CartRetailIR(std::unique_ptr&& rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); ~CartRetailIR() override; - virtual u32 Type() const override { return CartType::RetailIR; } - void Reset() override; void DoSavestate(Savestate* file) override; @@ -182,67 +227,228 @@ public: u8 SPIWrite(u8 val, u32 pos, bool last) override; private: - u32 IRVersion; - u8 IRCmd; + u32 IRVersion = 0; + u8 IRCmd = 0; }; // CartRetailBT - Pok�mon Typing Adventure (SPI BT controller) class CartRetailBT : public CartRetail { public: - CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); + CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); + CartRetailBT(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); ~CartRetailBT() override; - virtual u32 Type() const override { return CartType::RetailBT; } - - void Reset() override; - - void DoSavestate(Savestate* file) override; - u8 SPIWrite(u8 val, u32 pos, bool last) override; }; -// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI) -class CartHomebrew : public CartCommon +// CartSD -- any 'cart' with an SD card slot +class CartSD : public CartCommon { public: - CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); + CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + CartSD(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + ~CartSD() override; + + [[nodiscard]] const std::optional& GetSDCard() const noexcept { return SD; } + void SetSDCard(FATStorage&& sdcard) noexcept { SD = std::move(sdcard); } + void SetSDCard(std::optional&& sdcard) noexcept + { + SD = std::move(sdcard); + sdcard = std::nullopt; + // moving from an optional doesn't set it to nullopt, + // it just leaves behind an optional with a moved-from value + } + + void SetSDCard(std::optional&& args) noexcept + { + // Close the open SD card (if any) so that its contents are flushed to disk. + // Also, if args refers to the same image file that SD is currently using, + // this will ensure that we don't have two open read-write handles + // to the same file. + SD = std::nullopt; + + if (args) + SD = FATStorage(std::move(*args)); + + args = std::nullopt; + // moving from an optional doesn't set it to nullopt, + // it just leaves behind an optional with a moved-from value + } + +protected: + void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const; + void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); + void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const; + + std::optional SD {}; +}; + +// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI) +class CartHomebrew : public CartSD +{ +public: + CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); ~CartHomebrew() override; - virtual u32 Type() const override { return CartType::Homebrew; } + void Reset() override; + void SetupDirectBoot(const std::string& romname, NDS& nds) override; + + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; +}; + +// CartR4 -- unlicensed R4 'cart' (NDSCartR4.cpp) +enum CartR4Type +{ + /* non-SDHC carts */ + CartR4TypeM3Simply = 0, + CartR4TypeR4 = 1, + /* SDHC carts */ + CartR4TypeAce3DS = 2 +}; + +enum CartR4Language +{ + CartR4LanguageJapanese = (7 << 3) | 1, + CartR4LanguageEnglish = (7 << 3) | 2, + CartR4LanguageFrench = (2 << 3) | 2, + CartR4LanguageKorean = (4 << 3) | 2, + CartR4LanguageSimplifiedChinese = (6 << 3) | 3, + CartR4LanguageTraditionalChinese = (7 << 3) | 3 +}; + +class CartR4 : public CartSD +{ +public: + CartR4(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, CartR4Type ctype, CartR4Language clanguage, + std::optional&& sdcard = std::nullopt); + ~CartR4() override; void Reset() override; - void SetupDirectBoot(const std::string& romname) override; void DoSavestate(Savestate* file) override; - int ROMCommandStart(u8* cmd, u8* data, u32 len) override; - void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; private: - void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly); - void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); - void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); + inline u32 GetAdjustedSector(u32 sector) const + { + return R4CartType >= CartR4TypeAce3DS ? sector : sector >> 9; + } - FATStorage* SD; - bool ReadOnly; + u16 GetEncryptionKey(u16 sector); + void ReadSDToBuffer(u32 sector, bool rom); + u64 SDFATEntrySectorGet(u32 entry, u32 addr); + + s32 EncryptionKey; + u32 FATEntryOffset[2]; + u8 Buffer[512]; + u8 InitStatus; + CartR4Type R4CartType; + CartR4Language CartLanguage; + bool BufferInitialized; }; -extern u16 SPICnt; -extern u32 ROMCnt; +class NDSCartSlot +{ +public: + explicit NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom = nullptr) noexcept; + ~NDSCartSlot() noexcept; + void Reset() noexcept; + void ResetCart() noexcept; + void DoSavestate(Savestate* file) noexcept; + void DecryptSecureArea(u8* out) noexcept; -extern u8 ROMCommand[8]; + /// Loads a Nintendo DS cart object into the cart slot. + /// The cart slot takes ownership of the cart object and its underlying resources + /// and re-encrypts the ROM's secure area if necessary. + /// If a cartridge is already inserted, it is first ejected + /// and its state is discarded. + /// If the provided cart is not valid, + /// then the currently-loaded ROM will not be ejected. + /// + /// @param cart Movable reference to the cart, + /// or \c nullptr to eject the cart. + /// @post If the cart was successfully loaded, + /// then \c cart will be \c nullptr + /// and \c Cart will contain the object that \c cart previously pointed to. + /// Otherwise, \c cart and \c Cart will be both be unchanged. + void SetCart(std::unique_ptr&& cart) noexcept; + [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } + [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } -/// The currently loaded NDS cart. -extern std::unique_ptr Cart; + void SetupDirectBoot(const std::string& romname) noexcept; -bool Init(); -void DeInit(); -void Reset(); + /// This function is intended to allow frontends to save and load SRAM + /// without using melonDS APIs. + /// Modifying the emulated SRAM for any other reason is strongly discouraged. + /// The returned pointer may be invalidated if the emulator is reset, + /// or when a new game is loaded. + /// Consequently, don't store the returned pointer for any longer than necessary. + /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. + [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + void SetSaveMemory(const u8* savedata, u32 savelen) noexcept; -void DoSavestate(Savestate* file); + /// @returns The length of the buffer returned by ::GetSaveMemory() + /// if a cart is loaded and supports SRAM, otherwise zero. + [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } -void DecryptSecureArea(u8* out); + /// @return The cart that was in the slot before it was ejected, + /// or \c nullptr if the slot was already empty. + std::unique_ptr EjectCart() noexcept; + u32 ReadROMData() noexcept; + void WriteROMData(u32 val) noexcept; + void WriteSPICnt(u16 val) noexcept; + void WriteROMCnt(u32 val) noexcept; + [[nodiscard]] u8 ReadSPIData() const noexcept; + void WriteSPIData(u8 val) noexcept; + + [[nodiscard]] u8 GetROMCommand(u8 index) const noexcept { return ROMCommand[index]; } + void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; } + + [[nodiscard]] u32 GetROMCnt() const noexcept { return ROMCnt; } + [[nodiscard]] u16 GetSPICnt() const noexcept { return SPICnt; } + void SetSPICnt(u16 val) noexcept { SPICnt = val; } +private: + friend class CartCommon; + melonDS::NDS& NDS; + u16 SPICnt = 0; + u32 ROMCnt = 0; + std::array ROMCommand {}; + u8 SPIData = 0; + u32 SPIDataPos = 0; + bool SPIHold = false; + + u32 ROMData = 0; + + std::array TransferData {}; + u32 TransferPos = 0; + u32 TransferLen = 0; + u32 TransferDir = 0; + std::array TransferCmd {}; + + std::unique_ptr Cart = nullptr; + + std::array Key1_KeyBuf {}; + + u64 Key2_X = 0; + u64 Key2_Y = 0; + + void Key1_Encrypt(u32* data) const noexcept; + void Key1_Decrypt(u32* data) const noexcept; + void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept; + void Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept; + void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept; + void Key2_Encrypt(const u8* data, u32 len) noexcept; + void ROMEndTransfer(u32 param) noexcept; + void ROMPrepareData(u32 param) noexcept; + void AdvanceROMTransfer() noexcept; + void SPITransferDone(u32 param) noexcept; +}; /// Parses the given ROM data and constructs a \c NDSCart::CartCommon subclass /// that can be inserted into the emulator or used to extract information about the cart beforehand. @@ -250,68 +456,13 @@ void DecryptSecureArea(u8* out); /// The returned cartridge will contain a copy of this data, /// so the caller may deallocate \c romdata after this function returns. /// @param romlen The length of the ROM data in bytes. +/// @param sdcard The arguments to use for initializing the SD card. +/// Ignored if the parsed ROM is not homebrew. +/// If not given, the cart will not have an SD card. /// @returns A \c NDSCart::CartCommon object representing the parsed ROM, /// or \c nullptr if the ROM data couldn't be parsed. -std::unique_ptr ParseROM(const u8* romdata, u32 romlen); - -/// Loads a Nintendo DS cart object into the emulator. -/// The emulator takes ownership of the cart object and its underlying resources -/// and re-encrypts the ROM's secure area if necessary. -/// If a cartridge is already inserted, it is first ejected -/// and its state is discarded. -/// If the provided cart is not valid, -/// then the currently-loaded ROM will not be ejected. -/// -/// @param cart Movable reference to the cart. -/// @returns \c true if the cart was successfully loaded, -/// \c false otherwise. -/// @post If the cart was successfully loaded, -/// then \c cart will be \c nullptr -/// and \c Cart will contain the object that \c cart previously pointed to. -/// Otherwise, \c cart and \c Cart will be both be unchanged. -bool InsertROM(std::unique_ptr&& cart); - -/// Parses a ROM image and loads it into the emulator. -/// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence. -/// @param romdata Pointer to the ROM image. -/// The cart emulator maintains its own copy of this data, -/// so the caller is free to discard this data after calling this function. -/// @param romlen The length of the ROM image, in bytes. -/// @returns \c true if the ROM image was successfully loaded, -/// \c false if not. -bool LoadROM(const u8* romdata, u32 romlen); -void LoadSave(const u8* savedata, u32 savelen); -void SetupDirectBoot(const std::string& romname); - -/// This function is intended to allow frontends to save and load SRAM -/// without using melonDS APIs. -/// Modifying the emulated SRAM for any other reason is strongly discouraged. -/// The returned pointer may be invalidated if the emulator is reset, -/// or when a new game is loaded. -/// Consequently, don't store the returned pointer for any longer than necessary. -/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. -u8* GetSaveMemory(); - -/// @returns The length of the buffer returned by ::GetSaveMemory() -/// if a cart is loaded and supports SRAM, otherwise zero. -u32 GetSaveMemoryLength(); - -void EjectCart(); - -void ResetCart(); - -void WriteROMCnt(u32 val); -u32 ReadROMData(); -void WriteROMData(u32 val); - -void WriteSPICnt(u16 val); -u8 ReadSPIData(); -void WriteSPIData(u8 val); - -void ROMPrepareData(u32 param); -void ROMEndTransfer(u32 param); -void SPITransferDone(u32 param); - +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, std::optional&& args = std::nullopt); +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::optional&& args = std::nullopt); } #endif diff --git a/src/NDSCartR4.cpp b/src/NDSCartR4.cpp new file mode 100644 index 00000000..8497f556 --- /dev/null +++ b/src/NDSCartR4.cpp @@ -0,0 +1,371 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include "NDS.h" +#include "DSi.h" +#include "NDSCart.h" +#include "Platform.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; + +namespace NDSCart +{ + +/* + Original algorithm discovered by yasu, 2007 + + http://hp.vector.co.jp/authors/VA013928/ + http://www.usay.jp/ + http://www.yasu.nu/ +*/ +static void DecryptR4Sector(u8* dest, u8* src, u16 key1) +{ + for (int i = 0; i < 512; i++) + { + // Derive key2 from key1. + u8 key2 = ((key1 >> 7) & 0x80) + | ((key1 >> 6) & 0x60) + | ((key1 >> 5) & 0x10) + | ((key1 >> 4) & 0x0C) + | (key1 & 0x03); + + // Decrypt byte. + dest[i] = src[i] ^ key2; + + // Derive next key1 from key2. + u16 tmp = ((src[i] << 8) ^ key1); + u16 tmpXor = 0; + for (int ii = 0; ii < 16; ii++) + tmpXor ^= (tmp >> ii); + + u16 newKey1 = 0; + newKey1 |= ((tmpXor & 0x80) | (tmp & 0x7C)) << 8; + newKey1 |= ((tmp ^ (tmpXor >> 14)) << 8) & 0x0300; + newKey1 |= (((tmp >> 1) ^ tmp) >> 6) & 0xFC; + newKey1 |= ((tmp ^ (tmpXor >> 1)) >> 8) & 0x03; + + key1 = newKey1; + } +} + +CartR4::CartR4(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, CartR4Type ctype, CartR4Language clanguage, + std::optional&& sdcard) + : CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard)) +{ + InitStatus = 0; + R4CartType = ctype; + CartLanguage = clanguage; +} + +CartR4::~CartR4() +{ +} + +void CartR4::Reset() +{ + CartSD::Reset(); + + BufferInitialized = false; + EncryptionKey = -1; + FATEntryOffset[0] = 0xFFFFFFFF; + FATEntryOffset[1] = 0xFFFFFFFF; + + if (!SD) + InitStatus = 1; + else + { + u8 buffer[512]; + if (!SD->ReadFile("_DS_MENU.DAT", 0, 512, buffer)) + InitStatus = 3; + else + InitStatus = 4; + } +} + +void CartR4::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->Var32(&FATEntryOffset[0]); + file->Var32(&FATEntryOffset[1]); + file->VarArray(Buffer, 512); +} + +// FIXME: Ace3DS/clone behavior is only partially verified. +int CartR4::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); + + switch (cmd[0]) + { + case 0xB0: /* Get card information */ + { + u32 info = 0x75A00000 | (((R4CartType >= 1 ? 4 : 0) | CartLanguage) << 3) | InitStatus; + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = info; + return 0; + } + case 0xB4: /* FAT entry */ + { + u8 entryBuffer[512]; + u32 sector = ((cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]) & (~0x1F); + // set FAT entry offset to the starting cluster, to gain a bit of speed + SD->ReadSectors(sector >> 9, 1, entryBuffer); + u16 fileEntryOffset = sector & 0x1FF; + u32 clusterStart = (entryBuffer[fileEntryOffset + 27] << 8) + | entryBuffer[fileEntryOffset + 26] + | (entryBuffer[fileEntryOffset + 21] << 24) + | (entryBuffer[fileEntryOffset + 20] << 16); + FATEntryOffset[cmd[4] & 0x01] = clusterStart; + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xB8: /* ? Get chip ID ? */ + { + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = ChipID; + return 0; + } + case 0xB2: /* Save read request */ + case 0xB6: /* ROM read request */ + { + u32 sector = ((cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]); + ReadSDToBuffer(sector, cmd[0] == 0xB6); + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xB9: /* SD read request */ + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (SD) + SD->ReadSectors(GetAdjustedSector(sector), 1, Buffer); + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xBB: /* SD write start */ + case 0xBD: /* Save write start */ + return 1; + case 0xBC: /* SD write status */ + case 0xBE: /* Save write status */ + { + if (R4CartType == CartR4TypeAce3DS && cmd[0] == 0xBC) + { + uint8_t checksum = 0; + for (int i = 0; i < 7; i++) + checksum ^= cmd[i]; + if (checksum != cmd[7]) + Log(LogLevel::Warn, "R4: invalid 0xBC command checksum (%d != %d)", cmd[7], checksum); + } + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xB7: /* ROM read data */ + { + /* If the buffer has not been initialized yet, emulate ROM. */ + /* TODO: When does the R4 do this exactly? */ + if (!BufferInitialized) + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memcpy(data, &ROM[addr & (ROMLength-1)], len); + return 0; + } + /* Otherwise, fall through. */ + } + case 0xB3: /* Save read data */ + case 0xBA: /* SD read data */ + { + // TODO: Do these use separate buffers? + for (u32 pos = 0; pos < len; pos++) + data[pos] = Buffer[pos & 0x1FF]; + return 0; + } + case 0xBF: /* ROM read decrypted data */ + { + // TODO: Is decryption done using the sector from 0xBF or 0xB6? + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (len >= 512) + DecryptR4Sector(data, Buffer, GetEncryptionKey(sector >> 9)); + return 0; + } + default: + Log(LogLevel::Warn, "R4: unknown command %02X %02X %02X %02X %02X %02X %02X %02X (%d)\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], len); + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } +} + +void CartR4::ROMCommandFinish(const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); + + switch (cmd[0]) + { + case 0xBB: /* SD write start */ + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + + // The official R4 firmware sends a superfluous write to card + // (sector 0, byte 1) on boot. TODO: Is this correct? + if (GetAdjustedSector(sector) != sector && (sector & 0x1FF)) break; + + if (SD && !SD->IsReadOnly()) + SD->WriteSectors(GetAdjustedSector(sector), 1, data); + break; + } + case 0xBD: /* Save write start */ + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + + if (sector & 0x1FF) break; + + if (SD && !SD->IsReadOnly()) + SD->WriteSectors( + SDFATEntrySectorGet(FATEntryOffset[1], sector) >> 9, + 1, data + ); + break; + } + } +} + +u16 CartR4::GetEncryptionKey(u16 sector) +{ + if (EncryptionKey == -1) + { + u8 encryptedBuffer[512]; + u8 decryptedBuffer[512]; + SD->ReadFile("_DS_MENU.DAT", 0, 512, encryptedBuffer); + for (u32 key = 0; key < 0x10000; key++) + { + DecryptR4Sector(decryptedBuffer, encryptedBuffer, key); + if (decryptedBuffer[12] == '#' && decryptedBuffer[13] == '#' + && decryptedBuffer[14] == '#' && decryptedBuffer[15] == '#') + { + EncryptionKey = key; + break; + } + } + + if (EncryptionKey != -1) + { + Log(LogLevel::Warn, "R4: found cartridge key = %04X\n", EncryptionKey); + } + else + { + EncryptionKey = -2; + Log(LogLevel::Warn, "R4: could not find cartridge key\n"); + } + } + return EncryptionKey ^ sector; +} + +void CartR4::ReadSDToBuffer(u32 sector, bool rom) +{ + if (SD) + { + if (rom && FATEntryOffset[0] == 0xFFFFFFFF) + { + // On first boot, read from _DS_MENU.DAT. + SD->ReadFile("_DS_MENU.DAT", sector & ~0x1FF, 512, Buffer); + } + else + { + // Default mode. + SD->ReadSectors( + SDFATEntrySectorGet(FATEntryOffset[rom ? 0 : 1], sector) >> 9, + 1, Buffer + ); + } + BufferInitialized = true; + } +} + +u64 CartR4::SDFATEntrySectorGet(u32 entry, u32 addr) +{ + u8 buffer[512]; + u32 bufferSector = 0xFFFFFFFF; + + // Parse FAT header. + SD->ReadSectors(0, 1, buffer); + u16 bytesPerSector = (buffer[12] << 8) | buffer[11]; + u8 sectorsPerCluster = buffer[13]; + u16 firstFatSector = (buffer[15] << 8) | buffer[14]; + u8 fatTableCount = buffer[16]; + u32 clustersTotal = SD->GetSectorCount() / sectorsPerCluster; + bool isFat32 = clustersTotal >= 65526; + + u32 fatTableSize = (buffer[23] << 8) | buffer[22]; + if (fatTableSize == 0 && isFat32) + fatTableSize = (buffer[39] << 24) | (buffer[38] << 16) | (buffer[37] << 8) | buffer[36]; + u32 bytesPerCluster = bytesPerSector * sectorsPerCluster; + u32 rootDirSectors = 0; + if (!isFat32) { + u32 rootDirEntries = (buffer[18] << 8) | buffer[17]; + rootDirSectors = ((rootDirEntries * 32) + (bytesPerSector - 1)) / bytesPerSector; + } + u32 firstDataSector = firstFatSector + fatTableCount * fatTableSize + rootDirSectors; + + // Parse file entry (done when processing command 0xB4). + u32 clusterStart = entry; + + // Parse cluster table. + u32 currentCluster = clusterStart; + while (true) + { + currentCluster &= isFat32 ? 0x0FFFFFFF : 0xFFFF; + if (addr < bytesPerCluster) + { + // Read from this cluster. + return (u64) (firstDataSector + ((currentCluster - 2) * sectorsPerCluster)) * bytesPerSector + addr; + } + else if (currentCluster >= 2 && currentCluster <= (isFat32 ? 0x0FFFFFF6 : 0xFFF6)) + { + // Follow into next cluster. + u32 nextClusterOffset = firstFatSector * bytesPerSector + currentCluster * (isFat32 ? 4 : 2); + u32 nextClusterTableSector = nextClusterOffset >> 9; + if (bufferSector != nextClusterTableSector) + { + SD->ReadSectors(nextClusterTableSector, 1, buffer); + bufferSector = nextClusterTableSector; + } + nextClusterOffset &= 0x1FF; + currentCluster = (buffer[nextClusterOffset + 1] << 8) | buffer[nextClusterOffset]; + if (isFat32) + currentCluster |= (buffer[nextClusterOffset + 3] << 24) | (buffer[nextClusterOffset + 2] << 16); + addr -= bytesPerCluster; + } + else + { + // End of cluster table. + return 0; + } + } +} + +} +} diff --git a/src/NDS_Header.h b/src/NDS_Header.h index 4496b957..77a5baca 100644 --- a/src/NDS_Header.h +++ b/src/NDS_Header.h @@ -22,6 +22,25 @@ #include #include "types.h" +namespace melonDS +{ +/// Set to indicate the console regions that a ROM (including DSiWare) +/// can be played on. +enum RegionMask : u32 +{ + NoRegion = 0, + Japan = 1 << 0, + USA = 1 << 1, + Europe = 1 << 2, + Australia = 1 << 3, + China = 1 << 4, + Korea = 1 << 5, + Reserved = ~(Japan | USA | Europe | Australia | China | Korea), + RegionFree = 0xFFFFFFFF, +}; + +constexpr u32 DSiWareTitleIDHigh = 0x00030004; + // Consult GBATEK for info on what these are struct NDSHeader { @@ -105,7 +124,7 @@ struct NDSHeader u8 DSiMBKWriteProtect[3]; // global MBK9 setting u8 DSiWRAMCntSetting; // global WRAMCNT setting - u32 DSiRegionMask; + RegionMask DSiRegionMask; u32 DSiPermissions[2]; u8 Reserved6[3]; u8 AppFlags; // flags at 1BF @@ -181,8 +200,9 @@ struct NDSHeader u8 HeaderSignature[128]; // RSA-SHA1 across 0x000..0xDFF - /// @return \c true if this header represents a DSi title - /// (either a physical cartridge or a DSiWare title). + /// @return \c true if this header represents a title + /// that is DSi-exclusive (including DSiWare) + /// or DSi-enhanced (including cartridges). [[nodiscard]] bool IsDSi() const { return (UnitCode & 0x02) != 0; } [[nodiscard]] u32 GameCodeAsU32() const { return (u32)GameCode[3] << 24 | @@ -196,7 +216,7 @@ struct NDSHeader } /// @return \c true if this header represents a DSiWare title. - [[nodiscard]] bool IsDSiWare() const { return IsDSi() && DSiRegionStart == 0; } + [[nodiscard]] bool IsDSiWare() const { return IsDSi() && DSiTitleIDHigh == DSiWareTitleIDHigh; } }; static_assert(sizeof(NDSHeader) == 4096, "NDSHeader is not 4096 bytes!"); @@ -227,5 +247,6 @@ struct NDSBanner static_assert(sizeof(NDSBanner) == 9152, "NDSBanner is not 9152 bytes!"); +} #endif //NDS_HEADER_H diff --git a/src/NonStupidBitfield.h b/src/NonStupidBitfield.h index 6d9cd2c0..67f999e1 100644 --- a/src/NonStupidBitfield.h +++ b/src/NonStupidBitfield.h @@ -55,6 +55,8 @@ inline u64 GetRangedBitMask(u32 idx, u32 startBit, u32 bitsCount) // like std::bitset but less stupid and optimised for // our use case (keeping track of memory invalidations) +namespace melonDS +{ template struct NonStupidBitField { @@ -66,7 +68,7 @@ struct NonStupidBitField NonStupidBitField& BitField; u32 Idx; - operator bool() + operator bool() const { return BitField.Data[Idx >> 6] & (1ULL << (Idx & 0x3F)); } @@ -86,13 +88,13 @@ struct NonStupidBitField u32 BitIdx; u64 RemainingBits; - u32 operator*() { return DataIdx * 64 + BitIdx; } + u32 operator*() const { return DataIdx * 64 + BitIdx; } - bool operator==(const Iterator& other) + bool operator==(const Iterator& other) const { return other.DataIdx == DataIdx; } - bool operator!=(const Iterator& other) + bool operator!=(const Iterator& other) const { return other.DataIdx != DataIdx; } @@ -269,5 +271,6 @@ struct NonStupidBitField } }; +} #endif diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index 0c793fac..d18fd730 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -26,6 +26,9 @@ #define XXH_STATIC_LINKING_ONLY #include "xxhash/xxhash.h" +namespace melonDS +{ + using Platform::Log; using Platform::LogLevel; @@ -209,6 +212,13 @@ bool CompilerShader(GLuint& id, const std::string& source, const std::string& na Log(LogLevel::Debug, "shader source:\n--\n%s\n--\n", source.c_str()); delete[] log; + Platform::FileHandle* logf = Platform::OpenFile("shaderfail.log", Platform::FileMode::WriteText); + Platform::FileWrite(fs, len+1, 1, logf); + Platform::CloseFile(logf); + + glDeleteShader(ids[0]); + glDeleteShader(ids[1]); + return false; } @@ -354,3 +364,5 @@ error: } } + +} \ No newline at end of file diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index ec2cb1f1..f8c44300 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -25,8 +25,7 @@ #include "Platform.h" #include "PlatformOGL.h" - -namespace OpenGL +namespace melonDS::OpenGL { void LoadShaderCache(); diff --git a/src/Platform.h b/src/Platform.h index 97522393..425c712c 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,10 +24,9 @@ #include #include -namespace SPI_Firmware +namespace melonDS { - class Firmware; -} +class Firmware; namespace Platform { @@ -93,57 +92,6 @@ int InstanceID(); */ std::string InstanceFileSuffix(); -// configuration values - -enum ConfigEntry -{ -#ifdef JIT_ENABLED - JIT_Enable, - JIT_MaxBlockSize, - JIT_LiteralOptimizations, - JIT_BranchOptimizations, - JIT_FastMemory, -#endif - - ExternalBIOSEnable, - - DSi_NANDPath, - - DLDI_Enable, - DLDI_ImagePath, - DLDI_ImageSize, - DLDI_ReadOnly, - DLDI_FolderSync, - DLDI_FolderPath, - - DSiSD_Enable, - DSiSD_ImagePath, - DSiSD_ImageSize, - DSiSD_ReadOnly, - DSiSD_FolderSync, - DSiSD_FolderPath, - - Firm_OverrideSettings [[deprecated("Individual fields can now be overridden")]], - Firm_Username, - Firm_Language, - Firm_BirthdayMonth, - Firm_BirthdayDay, - Firm_Color, - Firm_Message, - Firm_MAC, - - WifiSettingsPath, - - AudioBitDepth, - - DSi_FullBIOSBoot -}; - -int GetConfigInt(ConfigEntry entry); -bool GetConfigBool(ConfigEntry entry); -std::string GetConfigString(ConfigEntry entry); -bool GetConfigArray(ConfigEntry entry, void* data); - /** * Denotes how a file will be opened and accessed. * Flags may or may not correspond to the operating system's file API. @@ -188,6 +136,11 @@ enum FileMode : unsigned { */ Text = 0b01'00'00, + /** + * Opens a file in append mode. + */ + Append = 0b10'00'00, + /** * Opens a file for reading and writing. * Equivalent to Read | Write. @@ -253,6 +206,13 @@ FileHandle* OpenLocalFile(const std::string& path, FileMode mode); bool FileExists(const std::string& name); bool LocalFileExists(const std::string& name); +// Returns true if we have permission to write to the file. +// Warning: Also creates the file if not present! +bool CheckFileWritable(const std::string& filepath); + +// Same as above (CheckFileWritable()) but for local files. +bool CheckLocalFileWritable(const std::string& filepath); + /** Close a file opened with \c OpenFile. * @returns \c true if the file was closed successfully, false otherwise. * @post \c file is no longer valid and should not be used. @@ -336,7 +296,10 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen /// @param firmware The firmware that was just written. /// @param writeoffset The offset of the first byte that was written to firmware. /// @param writelen The number of bytes that were written to firmware. -void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 writelen); +void WriteFirmware(const Firmware& firmware, u32 writeoffset, u32 writelen); + +// called when the RTC date/time is changed and the frontend might need to take it into account +void WriteDateTime(int year, int month, int day, int hour, int minute, int second); // local multiplayer comm interface @@ -395,4 +358,5 @@ void DynamicLibrary_Unload(DynamicLibrary* lib); void* DynamicLibrary_LoadFunction(DynamicLibrary* lib, const char* name); } +} #endif // PLATFORM_H diff --git a/src/ROMList.cpp b/src/ROMList.cpp index 725190aa..3ff771f8 100644 --- a/src/ROMList.cpp +++ b/src/ROMList.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -18,6 +18,8 @@ #include "ROMList.h" +namespace melonDS +{ const ROMListEntry ROMList[] = { {0x41464141, 0x00800000, 0x00000004}, @@ -6800,4 +6802,6 @@ const ROMListEntry ROMList[] = {0x5A5A5242, 0x04000000, 0x00000003}, }; -const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry); \ No newline at end of file +const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry); + +} \ No newline at end of file diff --git a/src/ROMList.h b/src/ROMList.h index c1bce154..82ee0ccf 100644 --- a/src/ROMList.h +++ b/src/ROMList.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -23,6 +23,8 @@ #include "types.h" +namespace melonDS +{ struct ROMListEntry { u32 GameCode; @@ -36,4 +38,5 @@ extern const ROMListEntry ROMList[]; /// The number of elements in \c ROMList. extern const size_t ROMListEntryCount; +} #endif // ROMLIST_H diff --git a/src/RTC.cpp b/src/RTC.cpp index 94d7ae7c..d8219df1 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -16,53 +16,39 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ -// Required by MinGW to enable localtime_r in time.h -#define _POSIX_THREAD_SAFE_FUNCTIONS - #include -#include +#include "NDS.h" #include "RTC.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace RTC + + + +void WriteDateTime(int num, u8 val); + + +RTC::RTC(melonDS::NDS& nds) : NDS(nds) { + NDS.RegisterEventFunc(Event_RTC, 0, MemberEventFunc(RTC, ClockTimer)); -/// This value represents the Nintendo DS IO register, -/// \em not the value of the system's clock. -/// The actual system time is taken directly from the host. -u16 IO; + ResetState(); -u8 Input; -u32 InputBit; -u32 InputPos; - -u8 Output[8]; -u32 OutputBit; -u32 OutputPos; - -u8 CurCmd; - -u8 StatusReg1; -u8 StatusReg2; -u8 Alarm1[3]; -u8 Alarm2[3]; -u8 ClockAdjust; -u8 FreeReg; - - -bool Init() -{ - return true; + // indicate the power was off + // this will be changed if a previously saved RTC state is loaded + State.StatusReg1 = 0x80; } -void DeInit() +RTC::~RTC() { + NDS.UnregisterEventFunc(Event_RTC, 0); } -void Reset() +void RTC::Reset() { Input = 0; InputBit = 0; @@ -73,15 +59,11 @@ void Reset() CurCmd = 0; - StatusReg1 = 0; - StatusReg2 = 0; - memset(Alarm1, 0, sizeof(Alarm1)); - memset(Alarm2, 0, sizeof(Alarm2)); - ClockAdjust = 0; - FreeReg = 0; + ClockCount = 0; + ScheduleTimer(true); } -void DoSavestate(Savestate* file) +void RTC::DoSavestate(Savestate* file) { file->Section("RTC."); @@ -97,22 +79,768 @@ void DoSavestate(Savestate* file) file->Var8(&CurCmd); - file->Var8(&StatusReg1); - file->Var8(&StatusReg2); - file->VarArray(Alarm1, sizeof(Alarm1)); - file->VarArray(Alarm2, sizeof(Alarm2)); - file->Var8(&ClockAdjust); - file->Var8(&FreeReg); + file->VarArray(&State, sizeof(State)); + + file->Var32((u32*)&TimerError); + file->Var32(&ClockCount); } -u8 BCD(u8 val) +u8 RTC::BCD(u8 val) const { return (val % 10) | ((val / 10) << 4); } +u8 RTC::FromBCD(u8 val) const +{ + return (val & 0xF) + ((val >> 4) * 10); +} -void ByteIn(u8 val) +u8 RTC::BCDIncrement(u8 val) const +{ + val++; + if ((val & 0x0F) >= 0x0A) + val += 0x06; + if ((val & 0xF0) >= 0xA0) + val += 0x60; + return val; +} + +u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax) const +{ + if (val < vmin || val > vmax) + val = vmin; + else if ((val & 0x0F) >= 0x0A) + val = vmin; + else if ((val & 0xF0) >= 0xA0) + val = vmin; + + return val; +} + + +void RTC::GetState(StateData& state) const +{ + memcpy(&state, &State, sizeof(State)); +} + +void RTC::SetState(const StateData& state) +{ + memcpy(&State, &state, sizeof(State)); + + // sanitize the input state + + for (int i = 0; i < 7; i++) + WriteDateTime(i+1, State.DateTime[i]); +} + +void RTC::GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) const +{ + year = FromBCD(State.DateTime[0]); + year += 2000; + month = FromBCD(State.DateTime[1] & 0x3F); + day = FromBCD(State.DateTime[2] & 0x3F); + + hour = FromBCD(State.DateTime[4] & 0x3F); + + if (!(State.StatusReg1 & (1<<1))) + { + // 12-hour mode + + if (State.DateTime[4] & 0x40) + hour += 12; + } + + minute = FromBCD(State.DateTime[5] & 0x7F); + second = FromBCD(State.DateTime[6] & 0x7F); +} + +void RTC::SetDateTime(int year, int month, int day, int hour, int minute, int second) +{ + int monthdays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + // the year range of the DS RTC is limited to 2000-2099 + year %= 100; + if (year < 0) year = 0; + + if (!(year & 3)) monthdays[2] = 29; + + if (month < 1 || month > 12) month = 1; + if (day < 1 || day > monthdays[month]) day = 1; + if (hour < 0 || hour > 23) hour = 0; + if (minute < 0 || minute > 59) minute = 0; + if (second < 0 || second > 59) second = 0; + + // note on day-of-week value + // that RTC register is a simple incrementing counter and the assignation is defined by software + // DS/DSi firmware counts from 0=Sunday + + int numdays = (year * 365) + ((year+3) / 4); // account for leap years + + for (int m = 1; m < month; m++) + { + numdays += monthdays[m]; + } + numdays += (day-1); + + // 01/01/2000 is a Saturday, so the starting value is 6 + int dayofweek = (6 + numdays) % 7; + + int pm = (hour >= 12) ? 0x40 : 0; + if (!(State.StatusReg1 & (1<<1))) + { + // 12-hour mode + + if (pm) hour -= 12; + } + + State.DateTime[0] = BCD(year); + State.DateTime[1] = BCD(month); + State.DateTime[2] = BCD(day); + State.DateTime[3] = dayofweek; + State.DateTime[4] = BCD(hour) | pm; + State.DateTime[5] = BCD(minute); + State.DateTime[6] = BCD(second); + + State.StatusReg1 &= ~0x80; +} + +void RTC::ResetState() +{ + memset(&State, 0, sizeof(State)); + State.DateTime[1] = 1; + State.DateTime[2] = 1; +} + + +void RTC::SetIRQ(u8 irq) +{ + u8 oldstat = State.IRQFlag; + State.IRQFlag |= irq; + State.StatusReg1 |= irq; + + if ((!(oldstat & 0x30)) && (State.IRQFlag & 0x30)) + { + if ((NDS.RCnt & 0xC100) == 0x8100) + { + // CHECKME: is the IRQ status readable in RCNT? + NDS.SetIRQ(1, IRQ_RTC); + } + } +} + +void RTC::ClearIRQ(u8 irq) +{ + State.IRQFlag &= ~irq; +} + +void RTC::ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write +{ + // INT1 + + switch (State.StatusReg2 & 0x0F) + { + case 0b0000: // none + if (type == 2) + { + ClearIRQ(0x10); + } + break; + + case 0b0001: + case 0b0101: // selected frequency steady interrupt + if ((type == 1 && (!(ClockCount & 0x3FF))) || (type == 2)) + { + u32 mask = 0; + if (State.Alarm1[2] & (1<<0)) mask |= 0x4000; + if (State.Alarm1[2] & (1<<1)) mask |= 0x2000; + if (State.Alarm1[2] & (1<<2)) mask |= 0x1000; + if (State.Alarm1[2] & (1<<3)) mask |= 0x0800; + if (State.Alarm1[2] & (1<<4)) mask |= 0x0400; + + if (mask && ((ClockCount & mask) != mask)) + SetIRQ(0x10); + else + ClearIRQ(0x10); + } + break; + + case 0b0010: + case 0b0110: // per-minute edge interrupt + if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01))) + { + SetIRQ(0x10); + } + break; + + case 0b0011: // per-minute steady interrupt 1 (duty 30s) + if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01))) + { + SetIRQ(0x10); + } + else if ((type == 1) && (State.DateTime[6] == 0x30) && ((ClockCount & 0x7FFF) == 0)) + { + ClearIRQ(0x10); + } + break; + + case 0b0111: // per-minute steady interrupt 2 (duty 256 cycles) + if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01))) + { + SetIRQ(0x10); + } + else if ((type == 1) && (State.DateTime[6] == 0x00) && ((ClockCount & 0x7FFF) == 256)) + { + ClearIRQ(0x10); + } + break; + + case 0b0100: // alarm interrupt + if (type == 0) + { + bool cond = true; + if (State.Alarm1[0] & (1<<7)) + cond = cond && ((State.Alarm1[0] & 0x07) == State.DateTime[3]); + if (State.Alarm1[1] & (1<<7)) + cond = cond && ((State.Alarm1[1] & 0x7F) == State.DateTime[4]); + if (State.Alarm1[2] & (1<<7)) + cond = cond && ((State.Alarm1[2] & 0x7F) == State.DateTime[5]); + + if (NDS.ConsoleType == 1) + { + if (State.AlarmDate1[1] & (1<<6)) + cond = cond && (State.AlarmDate1[0] == State.DateTime[0]); + if (State.AlarmDate1[1] & (1<<7)) + cond = cond && ((State.AlarmDate1[1] & 0x1F) == State.DateTime[1]); + if (State.AlarmDate1[2] & (1<<7)) + cond = cond && ((State.AlarmDate1[2] & 0x3F) == State.DateTime[2]); + } + + if (cond) + SetIRQ(0x10); + else + ClearIRQ(0x10); + } + break; + + default: // 32KHz output + if (type == 1) + { + SetIRQ(0x10); + ClearIRQ(0x10); + } + break; + } + + // INT2 + + if (State.StatusReg2 & (1<<6)) + { + // alarm interrupt + + if (type == 0) + { + bool cond = true; + if (State.Alarm2[0] & (1<<7)) + cond = cond && ((State.Alarm2[0] & 0x07) == State.DateTime[3]); + if (State.Alarm2[1] & (1<<7)) + cond = cond && ((State.Alarm2[1] & 0x7F) == State.DateTime[4]); + if (State.Alarm2[2] & (1<<7)) + cond = cond && ((State.Alarm2[2] & 0x7F) == State.DateTime[5]); + + if (NDS.ConsoleType == 1) + { + if (State.AlarmDate2[1] & (1<<6)) + cond = cond && (State.AlarmDate2[0] == State.DateTime[0]); + if (State.AlarmDate2[1] & (1<<7)) + cond = cond && ((State.AlarmDate2[1] & 0x1F) == State.DateTime[1]); + if (State.AlarmDate2[2] & (1<<7)) + cond = cond && ((State.AlarmDate2[2] & 0x3F) == State.DateTime[2]); + } + + if (cond) + SetIRQ(0x20); + else + ClearIRQ(0x20); + } + } + else + { + if (type == 2) + { + ClearIRQ(0x20); + } + } +} + + +u8 RTC::DaysInMonth() const +{ + u8 numdays; + + switch (State.DateTime[1]) + { + case 0x01: // Jan + case 0x03: // Mar + case 0x05: // May + case 0x07: // Jul + case 0x08: // Aug + case 0x10: // Oct + case 0x12: // Dec + numdays = 0x31; + break; + + case 0x04: // Apr + case 0x06: // Jun + case 0x09: // Sep + case 0x11: // Nov + numdays = 0x30; + break; + + case 0x02: // Feb + { + numdays = 0x28; + + // leap year: if year divisible by 4 and not divisible by 100 unless divisible by 400 + // the limited year range (2000-2099) simplifies this + int year = State.DateTime[0]; + year = (year & 0xF) + ((year >> 4) * 10); + if (!(year & 3)) + numdays = 0x29; + } + break; + + default: // ??? + return 0; + } + + return numdays; +} + +void RTC::CountYear() +{ + State.DateTime[0] = BCDIncrement(State.DateTime[0]); +} + +void RTC::CountMonth() +{ + State.DateTime[1] = BCDIncrement(State.DateTime[1]); + if (State.DateTime[1] > 0x12) + { + State.DateTime[1] = 1; + CountYear(); + } +} + +void RTC::CheckEndOfMonth() +{ + if (State.DateTime[2] > DaysInMonth()) + { + State.DateTime[2] = 1; + CountMonth(); + } +} + +void RTC::CountDay() +{ + // day-of-week counter + State.DateTime[3]++; + if (State.DateTime[3] >= 7) + State.DateTime[3] = 0; + + // day counter + State.DateTime[2] = BCDIncrement(State.DateTime[2]); + CheckEndOfMonth(); +} + +void RTC::CountHour() +{ + u8 hour = BCDIncrement(State.DateTime[4] & 0x3F); + u8 pm = State.DateTime[4] & 0x40; + + if (State.StatusReg1 & (1<<1)) + { + // 24-hour mode + + if (hour >= 0x24) + { + hour = 0; + CountDay(); + } + + pm = (hour >= 0x12) ? 0x40 : 0; + } + else + { + // 12-hour mode + + if (hour >= 0x12) + { + hour = 0; + if (pm) CountDay(); + pm ^= 0x40; + } + } + + State.DateTime[4] = hour | pm; +} + +void RTC::CountMinute() +{ + State.MinuteCount++; + State.DateTime[5] = BCDIncrement(State.DateTime[5]); + if (State.DateTime[5] >= 0x60) + { + State.DateTime[5] = 0; + CountHour(); + } + + State.IRQFlag |= 0x01; // store minute carry flag + ProcessIRQ(0); +} + +void RTC::CountSecond() +{ + State.DateTime[6] = BCDIncrement(State.DateTime[6]); + if (State.DateTime[6] >= 0x60) + { + State.DateTime[6] = 0; + CountMinute(); + } +} + + +void RTC::ScheduleTimer(bool first) +{ + if (first) TimerError = 0; + + // the RTC clock runs at 32768Hz + // cycles = 33513982 / 32768 + s32 sysclock = 33513982 + TimerError; + s32 delay = sysclock >> 15; + TimerError = sysclock & 0x7FFF; + + NDS.ScheduleEvent(Event_RTC, !first, delay, 0, 0); +} + +void RTC::ClockTimer(u32 param) +{ + ClockCount++; + + if (!(ClockCount & 0x7FFF)) + { + // count up one second + CountSecond(); + } + else if ((ClockCount & 0x7FFF) == 4) + { + // minute-carry flag lasts 4 cycles + State.IRQFlag &= ~0x01; + } + + ProcessIRQ(1); + + ScheduleTimer(false); +} + + +void RTC::WriteDateTime(int num, u8 val) +{ + switch (num) + { + case 1: // year + State.DateTime[0] = BCDSanitize(val, 0x00, 0x99); + break; + + case 2: // month + State.DateTime[1] = BCDSanitize(val & 0x1F, 0x01, 0x12); + break; + + case 3: // day + State.DateTime[2] = BCDSanitize(val & 0x3F, 0x01, 0x31); + CheckEndOfMonth(); + break; + + case 4: // day of week + State.DateTime[3] = BCDSanitize(val & 0x07, 0x00, 0x06); + break; + + case 5: // hour + { + u8 hour = val & 0x3F; + u8 pm = val & 0x40; + + if (State.StatusReg1 & (1<<1)) + { + // 24-hour mode + + hour = BCDSanitize(hour, 0x00, 0x23); + pm = (hour >= 0x12) ? 0x40 : 0; + } + else + { + // 12-hour mode + + hour = BCDSanitize(hour, 0x00, 0x11); + } + + State.DateTime[4] = hour | pm; + } + break; + + case 6: // minute + State.DateTime[5] = BCDSanitize(val & 0x7F, 0x00, 0x59); + break; + + case 7: // second + State.DateTime[6] = BCDSanitize(val & 0x7F, 0x00, 0x59); + break; + } +} + +void RTC::SaveDateTime() +{ + int y, m, d, h, i, s; + GetDateTime(y, m, d, h, i, s); + Platform::WriteDateTime(y, m, d, h, i, s); +} + +void RTC::CmdRead() +{ + if ((CurCmd & 0x0F) == 0x06) + { + switch (CurCmd & 0x70) + { + case 0x00: + Output[0] = State.StatusReg1; + State.StatusReg1 &= 0x0F; // clear auto-clearing bit4-7 + break; + + case 0x40: + Output[0] = State.StatusReg2; + break; + + case 0x20: + memcpy(Output, &State.DateTime[0], 7); + break; + + case 0x60: + memcpy(Output, &State.DateTime[4], 3); + break; + + case 0x10: + if (State.StatusReg2 & 0x04) + memcpy(Output, &State.Alarm1[0], 3); + else + Output[0] = State.Alarm1[2]; + break; + + case 0x50: + memcpy(Output, &State.Alarm2[0], 3); + break; + + case 0x30: Output[0] = State.ClockAdjust; break; + case 0x70: Output[0] = State.FreeReg; break; + } + + return; + } + else if ((CurCmd & 0x0F) == 0x0E) + { + if (NDS.ConsoleType != 1) + { + Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); + return; + } + + switch (CurCmd & 0x70) + { + case 0x00: + Output[0] = (State.MinuteCount >> 16) & 0xFF; + Output[1] = (State.MinuteCount >> 8) & 0xFF; + Output[2] = State.MinuteCount & 0xFF; + break; + + case 0x40: Output[0] = State.FOUT1; break; + case 0x20: Output[0] = State.FOUT2; break; + + case 0x10: + memcpy(Output, &State.AlarmDate1[0], 3); + break; + + case 0x50: + memcpy(Output, &State.AlarmDate2[0], 3); + break; + + default: + Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); + break; + } + + return; + } + + Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); +} + +void RTC::CmdWrite(u8 val) +{ + if ((CurCmd & 0x0F) == 0x06) + { + switch (CurCmd & 0x70) + { + case 0x00: + if (InputPos == 1) + { + u8 oldval = State.StatusReg1; + + if (val & (1<<0)) // reset + ResetState(); + + State.StatusReg1 = (State.StatusReg1 & 0xF0) | (val & 0x0E); + + if ((State.StatusReg1 ^ oldval) & (1<<1)) + { + // AM/PM changed + + u8 hour = State.DateTime[4] & 0x3F; + u8 pm = State.DateTime[4] & 0x40; + + if (State.StatusReg1 & (1<<1)) + { + // 24-hour mode + + if (pm) + { + hour += 0x12; + if ((hour & 0x0F) >= 0x0A) + hour += 0x06; + } + + hour = BCDSanitize(hour, 0x00, 0x23); + } + else + { + // 12-hour mode + + if (hour >= 0x12) + { + pm = 0x40; + + hour -= 0x12; + if ((hour & 0x0F) >= 0x0A) + hour -= 0x06; + } + else + pm = 0; + + hour = BCDSanitize(hour, 0x00, 0x11); + } + + State.DateTime[4] = hour | pm; + } + } + break; + + case 0x40: + if (InputPos == 1) + { + State.StatusReg2 = val; + ProcessIRQ(2); + } + break; + + case 0x20: + if (InputPos <= 7) + WriteDateTime(InputPos, val); + if (InputPos == 7) + SaveDateTime(); + break; + + case 0x60: + if (InputPos <= 3) + WriteDateTime(InputPos+4, val); + if (InputPos == 3) + SaveDateTime(); + break; + + case 0x10: + if (State.StatusReg2 & 0x04) + { + if (InputPos <= 3) + State.Alarm1[InputPos-1] = val; + } + else + { + if (InputPos == 1) + State.Alarm1[2] = val; + } + break; + + case 0x50: + if (InputPos <= 3) + State.Alarm2[InputPos-1] = val; + break; + + case 0x30: + if (InputPos == 1) + State.ClockAdjust = val; + break; + + case 0x70: + if (InputPos == 1) + State.FreeReg = val; + break; + } + + return; + } + else if ((CurCmd & 0x0F) == 0x0E) + { + if (NDS.ConsoleType != 1) + { + Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); + return; + } + + switch (CurCmd & 0x70) + { + case 0x00: + Log(LogLevel::Debug, "RTC: trying to write read-only minute counter\n"); + break; + + case 0x40: + if (InputPos == 1) + State.FOUT1 = val; + break; + + case 0x20: + if (InputPos == 1) + State.FOUT2 = val; + break; + + case 0x10: + if (InputPos <= 3) + State.AlarmDate1[InputPos-1] = val; + break; + + case 0x50: + if (InputPos <= 3) + State.AlarmDate2[InputPos-1] = val; + break; + + default: + Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); + break; + } + + return; + } + + Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); +} + +void RTC::ByteIn(u8 val) { if (InputPos == 0) { @@ -124,117 +852,35 @@ void ByteIn(u8 val) else CurCmd = val; + if (NDS.ConsoleType == 1) + { + // for DSi: handle extra commands + + if (((CurCmd & 0xF0) == 0x70) && ((CurCmd & 0xFE) != 0x76)) + { + u8 rev[16] = {0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE}; + CurCmd = rev[CurCmd & 0xF]; + } + } + if (CurCmd & 0x80) { - switch (CurCmd & 0x70) - { - case 0x00: Output[0] = StatusReg1; break; - case 0x40: Output[0] = StatusReg2; break; - - case 0x20: - { - time_t timestamp = time(NULL); - struct tm timedata; - localtime_r(×tamp, &timedata); - - Output[0] = BCD(timedata.tm_year - 100); - Output[1] = BCD(timedata.tm_mon + 1); - Output[2] = BCD(timedata.tm_mday); - Output[3] = BCD(timedata.tm_wday); - Output[4] = BCD(timedata.tm_hour); - Output[5] = BCD(timedata.tm_min); - Output[6] = BCD(timedata.tm_sec); - } - break; - - case 0x60: - { - time_t timestamp = time(NULL); - struct tm timedata; - localtime_r(×tamp, &timedata); - - Output[0] = BCD(timedata.tm_hour); - Output[1] = BCD(timedata.tm_min); - Output[2] = BCD(timedata.tm_sec); - } - break; - - case 0x10: - if (StatusReg2 & 0x04) - { - Output[0] = Alarm1[0]; - Output[1] = Alarm1[1]; - Output[2] = Alarm1[2]; - } - else - Output[0] = Alarm1[2]; - break; - - case 0x50: - Output[0] = Alarm2[0]; - Output[1] = Alarm2[1]; - Output[2] = Alarm2[2]; - break; - - case 0x30: Output[0] = ClockAdjust; break; - case 0x70: Output[0] = FreeReg; break; - } + CmdRead(); } return; } - switch (CurCmd & 0x70) - { - case 0x00: - if (InputPos == 1) StatusReg1 = val & 0x0E; - break; - - case 0x40: - if (InputPos == 1) StatusReg2 = val; - if (StatusReg2 & 0x4F) Log(LogLevel::Debug, "RTC INTERRUPT ON: %02X\n", StatusReg2); - break; - - case 0x20: - // TODO: set time somehow?? - break; - - case 0x60: - // same shit - break; - - case 0x10: - if (StatusReg2 & 0x04) - { - if (InputPos <= 3) Alarm1[InputPos-1] = val; - } - else - { - if (InputPos == 1) Alarm1[2] = val; - } - break; - - case 0x50: - if (InputPos <= 3) Alarm2[InputPos-1] = val; - break; - - case 0x30: - if (InputPos == 1) ClockAdjust = val; - break; - - case 0x70: - if (InputPos == 1) FreeReg = val; - break; - } + CmdWrite(val); } -u16 Read() +u16 RTC::Read() { //printf("RTC READ %04X\n", IO); return IO; } -void Write(u16 val, bool byte) +void RTC::Write(u16 val, bool byte) { if (byte) val |= (IO & 0xFF00); @@ -297,4 +943,4 @@ void Write(u16 val, bool byte) IO = (IO & 0x0001) | (val & 0xFFFE); } -} +} \ No newline at end of file diff --git a/src/RTC.h b/src/RTC.h index 0fed9fd1..1477e0eb 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,17 +22,96 @@ #include "types.h" #include "Savestate.h" -namespace RTC +namespace melonDS { +class RTC +{ +public: -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); + struct StateData + { + u8 StatusReg1; + u8 StatusReg2; + u8 DateTime[7]; + u8 Alarm1[3]; + u8 Alarm2[3]; + u8 ClockAdjust; + u8 FreeReg; -u16 Read(); -void Write(u16 val, bool byte); + u8 IRQFlag; + + // DSi registers + u32 MinuteCount; + u8 FOUT1; + u8 FOUT2; + u8 AlarmDate1[3]; + u8 AlarmDate2[3]; + }; + + RTC(melonDS::NDS& nds); + ~RTC(); + + void Reset(); + + void DoSavestate(Savestate* file); + + void GetState(StateData& state) const; + void SetState(const StateData& state); + void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) const; + void SetDateTime(int year, int month, int day, int hour, int minute, int second); + + void ClockTimer(u32 param); + + u16 Read(); + void Write(u16 val, bool byte); + +private: + melonDS::NDS& NDS; + u16 IO; + + u8 Input; + u32 InputBit; + u32 InputPos; + + u8 Output[8]; + u32 OutputBit; + u32 OutputPos; + + u8 CurCmd; + + StateData State; + + s32 TimerError; + u32 ClockCount; + + void ResetState(); + void ScheduleTimer(bool first); + + u8 BCD(u8 val) const; + u8 FromBCD(u8 val) const; + u8 BCDIncrement(u8 val) const; + u8 BCDSanitize(u8 val, u8 vmin, u8 vmax) const; + + void SetIRQ(u8 irq); + void ClearIRQ(u8 irq); + void ProcessIRQ(int type); + + u8 DaysInMonth() const; + void CountYear(); + void CountMonth(); + void CheckEndOfMonth(); + void CountDay(); + void CountHour(); + void CountMinute(); + void CountSecond(); + + void WriteDateTime(int num, u8 val); + void SaveDateTime(); + + void CmdRead(); + void CmdWrite(u8 val); + void ByteIn(u8 val); +}; } - #endif diff --git a/src/SPI.cpp b/src/SPI.cpp index a755c440..2aa915c6 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -27,20 +27,10 @@ #include "DSi_SPI_TSC.h" #include "Platform.h" +namespace melonDS +{ using namespace Platform; -namespace SPI_Firmware -{ - -std::unique_ptr Firmware; - -u32 Hold; -u8 CurCmd; -u32 DataPos; -u8 Data; - -u8 StatusReg; -u32 Addr; u16 CRC16(const u8* data, u32 len, u32 start) { @@ -65,57 +55,26 @@ u16 CRC16(const u8* data, u32 len, u32 start) return start & 0xFFFF; } -bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) + + +bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) const { - u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset]; - u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start); + u16 crc_stored = *(u16*)&FirmwareData.Buffer()[crcoffset]; + u16 crc_calced = CRC16(&FirmwareData.Buffer()[offset], len, start); return (crc_stored == crc_calced); } -bool Init() +FirmwareMem::FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware) : SPIDevice(nds), FirmwareData(std::move(firmware)) { - return true; } -void DeInit() +FirmwareMem::~FirmwareMem() = default; + +void FirmwareMem::Reset() { - RemoveFirmware(); -} - -u32 FixFirmwareLength(u32 originalLength) -{ - if (originalLength != 0x20000 && originalLength != 0x40000 && originalLength != 0x80000) - { - Log(LogLevel::Warn, "Bad firmware size %d, ", originalLength); - - // pick the nearest power-of-two length - originalLength |= (originalLength >> 1); - originalLength |= (originalLength >> 2); - originalLength |= (originalLength >> 4); - originalLength |= (originalLength >> 8); - originalLength |= (originalLength >> 16); - originalLength++; - - // ensure it's a sane length - if (originalLength > 0x80000) originalLength = 0x80000; - else if (originalLength < 0x20000) originalLength = 0x20000; - - Log(LogLevel::Debug, "assuming %d\n", originalLength); - } - return originalLength; -} - -void Reset() -{ - if (!Firmware) - { - Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n"); - Firmware = std::make_unique(NDS::ConsoleType); - } - // fix touchscreen coords - for (UserData& u : Firmware->UserData()) + for (auto& u : FirmwareData.GetUserData()) { u.TouchCalibrationADC1[0] = 0; u.TouchCalibrationADC1[1] = 0; @@ -127,17 +86,17 @@ void Reset() u.TouchCalibrationPixel2[1] = 191; } - Firmware->UpdateChecksums(); + FirmwareData.UpdateChecksums(); // disable autoboot //Firmware[userdata+0x64] &= 0xBF; - MacAddress mac = Firmware->Header().MacAddress; + MacAddress mac = FirmwareData.GetHeader().MacAddr; Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); // verify shit - u32 mask = Firmware->Mask(); - Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD"); + u32 mask = FirmwareData.Mask(); + Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&FirmwareData.Buffer()[0x2C], 0x2A)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD"); @@ -150,14 +109,14 @@ void Reset() StatusReg = 0x00; } -void DoSavestate(Savestate* file) +void FirmwareMem::DoSavestate(Savestate* file) { file->Section("SPFW"); // CHECKME/TODO: trust the firmware to stay the same????? // embedding the whole firmware in the savestate would be derpo tho?? - file->Var32(&Hold); + file->Bool32(&Hold); file->Var8(&CurCmd); file->Var32(&DataPos); file->Var8(&Data); @@ -166,110 +125,64 @@ void DoSavestate(Savestate* file) file->Var32(&Addr); } -void SetupDirectBoot(bool dsi) +void FirmwareMem::SetupDirectBoot() { - const FirmwareHeader& header = Firmware->Header(); - const UserData& userdata = Firmware->EffectiveUserData(); - if (dsi) + const auto& header = FirmwareData.GetHeader(); + const auto& userdata = FirmwareData.GetEffectiveUserData(); + if (NDS.ConsoleType == 1) { + // The ARMWrite methods are virtual, they'll delegate to DSi if necessary for (u32 i = 0; i < 6; i += 2) - DSi::ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddress[i]); // MAC address + NDS.ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddr[i]); // MAC address // checkme - DSi::ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels + NDS.ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels for (u32 i = 0; i < 0x70; i += 4) - DSi::ARM9Write32(0x02FFFC80+i, *(u32*)&userdata.Bytes[i]); + NDS.ARM9Write32(0x02FFFC80+i, *(u32*)&userdata.Bytes[i]); } else { - NDS::ARM9Write32(0x027FF864, 0); - NDS::ARM9Write32(0x027FF868, header.UserSettingsOffset << 3); // user settings offset + NDS.ARM9Write32(0x027FF864, 0); + NDS.ARM9Write32(0x027FF868, header.UserSettingsOffset << 3); // user settings offset - NDS::ARM9Write16(0x027FF874, header.DataGfxChecksum); // CRC16 for data/gfx - NDS::ARM9Write16(0x027FF876, header.GUIWifiCodeChecksum); // CRC16 for GUI/wifi code + NDS.ARM9Write16(0x027FF874, header.DataGfxChecksum); // CRC16 for data/gfx + NDS.ARM9Write16(0x027FF876, header.GUIWifiCodeChecksum); // CRC16 for GUI/wifi code for (u32 i = 0; i < 0x70; i += 4) - NDS::ARM9Write32(0x027FFC80+i, *(u32*)&userdata.Bytes[i]); + NDS.ARM9Write32(0x027FFC80+i, *(u32*)&userdata.Bytes[i]); } } -const class Firmware* GetFirmware() +bool FirmwareMem::IsLoadedFirmwareBuiltIn() const { - return Firmware.get(); + return FirmwareData.GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER; } -bool IsLoadedFirmwareBuiltIn() +void FirmwareMem::Write(u8 val) { - return Firmware->Header().Identifier == GENERATED_FIRMWARE_IDENTIFIER; -} - -bool InstallFirmware(class Firmware&& firmware) -{ - if (!firmware.Buffer()) - { - Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n"); - return false; - } - - Firmware = std::make_unique(std::move(firmware)); - - FirmwareIdentifier id = Firmware->Header().Identifier; - Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]); - - return true; -} - -bool InstallFirmware(std::unique_ptr&& firmware) -{ - if (!firmware) - { - Log(LogLevel::Error, "SPI firmware: firmware is null!\n"); - return false; - } - - if (!firmware->Buffer()) - { - Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n"); - return false; - } - - Firmware = std::move(firmware); - - FirmwareIdentifier id = Firmware->Header().Identifier; - Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]); - - return true; -} - -void RemoveFirmware() -{ - Firmware.reset(); - Log(LogLevel::Debug, "Removed installed firmware (if any)\n"); -} - -u8 Read() -{ - return Data; -} - -void Write(u8 val, u32 hold) -{ - if (!hold) - { - if (!Hold) // commands with no paramters - CurCmd = val; - - Hold = 0; - } - - if (hold && (!Hold)) + if (!Hold) { CurCmd = val; - Hold = 1; + Hold = true; Data = 0; DataPos = 1; Addr = 0; + + // handle commands with no parameters + switch (CurCmd) + { + case 0x04: // write disable + StatusReg &= ~(1<<1); + Data = 0; + break; + + case 0x06: // write enable + StatusReg |= (1<<1); + Data = 0; + break; + } + return; } @@ -285,7 +198,7 @@ void Write(u8 val, u32 hold) } else { - Data = Firmware->Buffer()[Addr & Firmware->Mask()]; + Data = FirmwareData.Buffer()[Addr & FirmwareData.Mask()]; Addr++; } @@ -293,20 +206,10 @@ void Write(u8 val, u32 hold) } break; - case 0x04: // write disable - StatusReg &= ~(1<<1); - Data = 0; - break; - case 0x05: // read status reg Data = StatusReg; break; - case 0x06: // write enable - StatusReg |= (1<<1); - Data = 0; - break; - case 0x0A: // write { // TODO: what happens if you write too many bytes? (max 256, they say) @@ -318,7 +221,7 @@ void Write(u8 val, u32 hold) } else { - Firmware->Buffer()[Addr & Firmware->Mask()] = val; + FirmwareData.Buffer()[Addr & FirmwareData.Mask()] = val; Data = val; Addr++; } @@ -345,45 +248,38 @@ void Write(u8 val, u32 hold) Data = 0xFF; break; } +} - if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A)) +void FirmwareMem::Release() +{ + if (CurCmd == 0x02 || CurCmd == 0x0A) { // If the SPI firmware chip just finished a write... // We only notify the frontend of changes to the Wi-fi/userdata settings region // (although it might still decide to flush the whole thing) - u32 wifioffset = Firmware->WifiAccessPointOffset(); + u32 wifioffset = FirmwareData.GetWifiAccessPointOffset(); // Request that the start of the Wi-fi/userdata settings region // through the end of the firmware blob be flushed to disk - Platform::WriteFirmware(*Firmware, wifioffset, Firmware->Length() - wifioffset); + Platform::WriteFirmware(FirmwareData, wifioffset, FirmwareData.Length() - wifioffset); } + + SPIDevice::Release(); + CurCmd = 0; } -} - -namespace SPI_Powerman -{ - -u32 Hold; -u32 DataPos; -u8 Index; -u8 Data; - -u8 Registers[8]; -u8 RegMasks[8]; -bool Init() -{ - return true; -} - -void DeInit() +PowerMan::PowerMan(melonDS::NDS& nds) : SPIDevice(nds) { } -void Reset() +PowerMan::~PowerMan() { - Hold = 0; +} + +void PowerMan::Reset() +{ + Hold = false; Index = 0; Data = 0; @@ -399,14 +295,11 @@ void Reset() RegMasks[4] = 0x0F; } -bool GetBatteryLevelOkay() { return !Registers[1]; } -void SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; } - -void DoSavestate(Savestate* file) +void PowerMan::DoSavestate(Savestate* file) { file->Section("SPPW"); - file->Var32(&Hold); + file->Bool32(&Hold); file->Var32(&DataPos); file->Var8(&Index); file->Var8(&Data); @@ -415,22 +308,15 @@ void DoSavestate(Savestate* file) file->VarArray(RegMasks, 8); // is that needed?? } -u8 Read() -{ - return Data; -} +bool PowerMan::GetBatteryLevelOkay() const { return !Registers[1]; } +void PowerMan::SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; } -void Write(u8 val, u32 hold) +void PowerMan::Write(u8 val) { - if (!hold) - { - Hold = 0; - } - - if (hold && (!Hold)) + if (!Hold) { Index = val; - Hold = 1; + Hold = true; Data = 0; DataPos = 1; return; @@ -452,7 +338,7 @@ void Write(u8 val, u32 hold) switch (regid) { case 0: - if (val & 0x40) NDS::Stop(StopReason::PowerOff); // shutdown + if (val & 0x40) NDS.Stop(StopReason::PowerOff); // shutdown //printf("power %02X\n", val); break; case 4: @@ -465,35 +351,19 @@ void Write(u8 val, u32 hold) Data = 0; } -} -namespace SPI_TSC -{ - -u32 DataPos; -u8 ControlByte; -u8 Data; - -u16 ConvResult; - -u16 TouchX, TouchY; - -s16 MicBuffer[1024]; -int MicBufferLen; - - -bool Init() -{ - return true; -} - -void DeInit() +TSC::TSC(melonDS::NDS& nds) : SPIDevice(nds) { } -void Reset() +TSC::~TSC() { +} + +void TSC::Reset() +{ + Hold = false; ControlByte = 0; Data = 0; @@ -502,7 +372,7 @@ void Reset() MicBufferLen = 0; } -void DoSavestate(Savestate* file) +void TSC::DoSavestate(Savestate* file) { file->Section("SPTS"); @@ -513,7 +383,7 @@ void DoSavestate(Savestate* file) file->Var16(&ConvResult); } -void SetTouchCoords(u16 x, u16 y) +void TSC::SetTouchCoords(u16 x, u16 y) { // scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1) // scr.y = (adc.y-adc.y1) * (scr.y2-scr.y1) / (adc.y2-adc.y1) + (scr.y1-1) @@ -522,13 +392,19 @@ void SetTouchCoords(u16 x, u16 y) TouchX = x; TouchY = y; - if (y == 0xFFF) return; + if (y == 0xFFF) + { + // released + NDS.KeyInput |= (1 << (16+6)); + return; + } TouchX <<= 4; TouchY <<= 4; + NDS.KeyInput &= ~(1 << (16+6)); } -void MicInputFrame(s16* data, int samples) +void TSC::MicInputFrame(const s16* data, int samples) { if (!data) { @@ -541,12 +417,7 @@ void MicInputFrame(s16* data, int samples) MicBufferLen = samples; } -u8 Read() -{ - return Data; -} - -void Write(u8 val, u32 hold) +void TSC::Write(u8 val) { if (DataPos == 1) Data = (ConvResult >> 5) & 0xFF; @@ -572,7 +443,7 @@ void Write(u8 val, u32 hold) else { // 560190 cycles per frame - u32 cyclepos = (u32)NDS::GetSysClockCycles(2); + u32 cyclepos = (u32)NDS.GetSysClockCycles(2); u32 samplepos = (cyclepos * MicBufferLen) / 560190; if (samplepos >= MicBufferLen) samplepos = MicBufferLen-1; s16 sample = MicBuffer[samplepos]; @@ -599,75 +470,67 @@ void Write(u8 val, u32 hold) DataPos++; } + + +SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds) +{ + NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); + + Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware)); + Devices[SPIDevice_PowerMan] = new PowerMan(NDS); + + if (NDS.ConsoleType == 1) + Devices[SPIDevice_TSC] = new DSi_TSC(static_cast(NDS)); + else + Devices[SPIDevice_TSC] = new TSC(NDS); } - -namespace SPI +SPIHost::~SPIHost() { + for (int i = 0; i < SPIDevice_MAX; i++) + { + if (Devices[i]) + delete Devices[i]; -u16 Cnt; + Devices[i] = nullptr; + } -u32 CurDevice; // remove me - - -bool Init() -{ - if (!SPI_Firmware::Init()) return false; - if (!SPI_Powerman::Init()) return false; - if (!SPI_TSC::Init()) return false; - if (!DSi_SPI_TSC::Init()) return false; - - return true; + NDS.UnregisterEventFunc(Event_SPITransfer, 0); } -void DeInit() -{ - SPI_Firmware::DeInit(); - SPI_Powerman::DeInit(); - SPI_TSC::DeInit(); - DSi_SPI_TSC::DeInit(); -} - -void Reset() +void SPIHost::Reset() { Cnt = 0; - SPI_Firmware::Reset(); - SPI_Powerman::Reset(); - SPI_TSC::Reset(); - if (NDS::ConsoleType == 1) DSi_SPI_TSC::Reset(); + for (int i = 0; i < SPIDevice_MAX; i++) + { + Devices[i]->Reset(); + } } -void DoSavestate(Savestate* file) +void SPIHost::DoSavestate(Savestate* file) { file->Section("SPIG"); file->Var16(&Cnt); - file->Var32(&CurDevice); - SPI_Firmware::DoSavestate(file); - SPI_Powerman::DoSavestate(file); - SPI_TSC::DoSavestate(file); - if (NDS::ConsoleType == 1) DSi_SPI_TSC::DoSavestate(file); + for (int i = 0; i < SPIDevice_MAX; i++) + { + Devices[i]->DoSavestate(file); + } } -void WriteCnt(u16 val) +void SPIHost::WriteCnt(u16 val) { // turning it off should clear chipselect // TODO: confirm on hardware. libnds expects this, though. if ((Cnt & (1<<15)) && !(val & (1<<15))) { - switch (Cnt & 0x0300) + int dev = (Cnt >> 8) & 0x3; + if (dev < SPIDevice_MAX) { - case 0x0000: SPI_Powerman::Hold = 0; break; - case 0x0100: SPI_Firmware::Hold = 0; break; - case 0x0200: - if (NDS::ConsoleType == 1) - DSi_SPI_TSC::DataPos = 0; - else - SPI_TSC::DataPos = 0; - break; + Devices[dev]->Release(); } } @@ -678,54 +541,50 @@ void WriteCnt(u16 val) if (Cnt & (1<<7)) Log(LogLevel::Warn, "!! CHANGING SPICNT DURING TRANSFER: %04X\n", val); } -void TransferDone(u32 param) +void SPIHost::TransferDone(u32 param) { Cnt &= ~(1<<7); if (Cnt & (1<<14)) - NDS::SetIRQ(1, NDS::IRQ_SPI); + NDS.SetIRQ(1, IRQ_SPI); } -u8 ReadData() +u8 SPIHost::ReadData() const { if (!(Cnt & (1<<15))) return 0; if (Cnt & (1<<7)) return 0; // checkme - switch (Cnt & 0x0300) + int dev = (Cnt >> 8) & 0x3; + if (dev < SPIDevice_MAX) { - case 0x0000: return SPI_Powerman::Read(); - case 0x0100: return SPI_Firmware::Read(); - case 0x0200: - if (NDS::ConsoleType == 1) - return DSi_SPI_TSC::Read(); - else - return SPI_TSC::Read(); - default: return 0; + return Devices[dev]->Read(); } + + return 0; } -void WriteData(u8 val) +void SPIHost::WriteData(u8 val) { if (!(Cnt & (1<<15))) return; if (Cnt & (1<<7)) return; Cnt |= (1<<7); - switch (Cnt & 0x0300) + + int dev = (Cnt >> 8) & 0x3; + if (dev < SPIDevice_MAX) { - case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break; - case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break; - case 0x0200: - if (NDS::ConsoleType == 1) - DSi_SPI_TSC::Write(val, Cnt&(1<<11)); - else - SPI_TSC::Write(val, Cnt&(1<<11)); - break; - default: Log(LogLevel::Warn, "SPI to unknown device %04X %02X\n", Cnt, val); break; + Devices[dev]->Write(val); + if (!(Cnt & (1<<11))) // release chipselect + Devices[dev]->Release(); + } + else + { + Log(LogLevel::Warn, "SPI to unknown device %04X %02X\n", Cnt, val); } // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (Cnt & 0x3)); - NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, TransferDone, 0); + NDS.ScheduleEvent(Event_SPITransfer, false, delay, 0, 0); } -} +} \ No newline at end of file diff --git a/src/SPI.h b/src/SPI.h index d8152038..7ed889a4 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -28,61 +28,148 @@ #include "Savestate.h" #include "SPI_Firmware.h" -namespace SPI_Firmware +namespace melonDS { +enum +{ + SPIDevice_PowerMan = 0, + SPIDevice_FirmwareMem, + SPIDevice_TSC, + + SPIDevice_MAX +}; u16 CRC16(const u8* data, u32 len, u32 start); -void SetupDirectBoot(bool dsi); -u32 FixFirmwareLength(u32 originalLength); - -/// @return A pointer to the installed firmware blob if one exists, otherwise \c nullptr. -/// @warning The pointer refers to memory that melonDS owns. Do not deallocate it yourself. -/// @see InstallFirmware -const Firmware* GetFirmware(); - -bool IsLoadedFirmwareBuiltIn(); -bool InstallFirmware(Firmware&& firmware); -bool InstallFirmware(std::unique_ptr&& firmware); -void RemoveFirmware(); -} - -namespace SPI_Powerman +class SPIHost; +class NDS; +class SPIDevice { +public: + SPIDevice(melonDS::NDS& nds) : NDS(nds), Hold(false), DataPos(0) {} + virtual ~SPIDevice() {} + virtual void Reset() = 0; + virtual void DoSavestate(Savestate* file) = 0; -bool GetBatteryLevelOkay(); -void SetBatteryLevelOkay(bool okay); + virtual u8 Read() const { return Data; } + virtual void Write(u8 val) = 0; + virtual void Release() { Hold = false; DataPos = 0; } -} +protected: + melonDS::NDS& NDS; -namespace SPI_TSC + bool Hold; + u32 DataPos; + u8 Data; +}; + +class FirmwareMem : public SPIDevice { +public: + FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware); + ~FirmwareMem() override; + void Reset() override; + void DoSavestate(Savestate* file) override; -void SetTouchCoords(u16 x, u16 y); -void MicInputFrame(s16* data, int samples); + void SetupDirectBoot(); -u8 Read(); -void Write(u8 val, u32 hold); + Firmware& GetFirmware() noexcept { return FirmwareData; } + [[nodiscard]] const Firmware& GetFirmware() const noexcept { return FirmwareData; } + void SetFirmware(Firmware&& firmware) { FirmwareData = std::move(firmware); } + bool IsLoadedFirmwareBuiltIn() const; -} + void Write(u8 val) override; + void Release() override; -namespace SPI +private: + Firmware FirmwareData; + + u8 CurCmd; + + u8 StatusReg; + u32 Addr; + + bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) const; +}; + +class PowerMan : public SPIDevice { +public: + PowerMan(melonDS::NDS& nds); + ~PowerMan() override; + void Reset() override; + void DoSavestate(Savestate* file) override; -extern u16 Cnt; + bool GetBatteryLevelOkay() const; + void SetBatteryLevelOkay(bool okay); -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); + void Write(u8 val) override; -void WriteCnt(u16 val); +private: + u8 Index; -u8 ReadData(); -void WriteData(u8 val); + u8 Registers[8]; + u8 RegMasks[8]; +}; -void TransferDone(u32 param); +class TSC : public SPIDevice +{ +public: + TSC(melonDS::NDS& nds); + virtual ~TSC() override; + virtual void Reset() override; + virtual void DoSavestate(Savestate* file) override; + + virtual void SetTouchCoords(u16 x, u16 y); + virtual void MicInputFrame(const s16* data, int samples); + + virtual void Write(u8 val) override; + +protected: + u8 ControlByte; + + u16 ConvResult; + + u16 TouchX, TouchY; + + s16 MicBuffer[1024]; + int MicBufferLen; +}; + + +class SPIHost +{ +public: + SPIHost(melonDS::NDS& nds, Firmware&& firmware); + ~SPIHost(); + void Reset(); + void DoSavestate(Savestate* file); + + FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } + const FirmwareMem* GetFirmwareMem() const { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } + PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; } + const PowerMan* GetPowerMan() const { return (PowerMan*)Devices[SPIDevice_PowerMan]; } + TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; } + const TSC* GetTSC() const { return (TSC*)Devices[SPIDevice_TSC]; } + + const Firmware& GetFirmware() const { return GetFirmwareMem()->GetFirmware(); } + Firmware& GetFirmware() { return GetFirmwareMem()->GetFirmware(); } + void SetFirmware(Firmware&& firmware) { GetFirmwareMem()->SetFirmware(std::move(firmware)); } + + u16 ReadCnt() const { return Cnt; } + void WriteCnt(u16 val); + + u8 ReadData() const; + void WriteData(u8 val); + + void TransferDone(u32 param); + +private: + melonDS::NDS& NDS; + u16 Cnt; + + SPIDevice* Devices[3]; +}; } - #endif diff --git a/src/SPI_Firmware.cpp b/src/SPI_Firmware.cpp index 44fbfe7d..89c8fa61 100644 --- a/src/SPI_Firmware.cpp +++ b/src/SPI_Firmware.cpp @@ -18,6 +18,12 @@ #include "SPI_Firmware.h" #include "SPI.h" +#include "Platform.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; #include @@ -49,7 +55,7 @@ constexpr u8 CHANDATA[0x3C] constexpr u8 DEFAULT_UNUSED3[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; -SPI_Firmware::WifiAccessPoint::WifiAccessPoint() +Firmware::WifiAccessPoint::WifiAccessPoint() { memset(Bytes, 0, sizeof(Bytes)); Status = AccessPointStatus::NotConfigured; @@ -57,7 +63,7 @@ SPI_Firmware::WifiAccessPoint::WifiAccessPoint() UpdateChecksum(); } -SPI_Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype) +Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype) { memset(Bytes, 0, sizeof(Bytes)); strncpy(SSID, DEFAULT_SSID, sizeof(SSID)); @@ -67,25 +73,25 @@ SPI_Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype) UpdateChecksum(); } -void SPI_Firmware::WifiAccessPoint::UpdateChecksum() +void Firmware::WifiAccessPoint::UpdateChecksum() { Checksum = CRC16(Bytes, 0xFE, 0x0000); } -SPI_Firmware::ExtendedWifiAccessPoint::ExtendedWifiAccessPoint() +Firmware::ExtendedWifiAccessPoint::ExtendedWifiAccessPoint() { Data.Base = WifiAccessPoint(); UpdateChecksum(); } -void SPI_Firmware::ExtendedWifiAccessPoint::UpdateChecksum() +void Firmware::ExtendedWifiAccessPoint::UpdateChecksum() { Data.Base.UpdateChecksum(); Data.ExtendedChecksum = CRC16(&Bytes[0x100], 0xFE, 0x0000); } -SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype) +Firmware::FirmwareHeader::FirmwareHeader(int consoletype) { if (consoletype == 1) { @@ -109,7 +115,7 @@ SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype) WifiConfigLength = 0x138; Unused1 = 0; memcpy(&Unused3, DEFAULT_UNUSED3, sizeof(DEFAULT_UNUSED3)); - MacAddress = DEFAULT_MAC; + MacAddr = DEFAULT_MAC; EnabledChannels = 0x3FFE; memset(&Unknown2, 0xFF, sizeof(Unknown2)); RFChipType = RFChipType::Type3; @@ -143,12 +149,12 @@ SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype) } -void SPI_Firmware::FirmwareHeader::UpdateChecksum() +void Firmware::FirmwareHeader::UpdateChecksum() { - WifiConfigChecksum = SPI_Firmware::CRC16(&Bytes[0x2C], WifiConfigLength, 0x0000); + WifiConfigChecksum = CRC16(&Bytes[0x2C], WifiConfigLength, 0x0000); } -SPI_Firmware::UserData::UserData() +Firmware::UserData::UserData() { memset(Bytes, 0, 0x74); Version = 5; @@ -160,7 +166,7 @@ SPI_Firmware::UserData::UserData() Checksum = CRC16(Bytes, 0x70, 0xFFFF); } -void SPI_Firmware::UserData::UpdateChecksum() +void Firmware::UserData::UpdateChecksum() { Checksum = CRC16(Bytes, 0x70, 0xFFFF); if (ExtendedSettings.Unknown0 == 0x01) @@ -169,7 +175,30 @@ void SPI_Firmware::UserData::UpdateChecksum() } } -SPI_Firmware::Firmware::Firmware(int consoletype) +u32 Firmware::FixFirmwareLength(u32 originalLength) +{ + if (originalLength != 0x20000 && originalLength != 0x40000 && originalLength != 0x80000) + { + Log(LogLevel::Warn, "Bad firmware size %d, ", originalLength); + + // pick the nearest power-of-two length + originalLength |= (originalLength >> 1); + originalLength |= (originalLength >> 2); + originalLength |= (originalLength >> 4); + originalLength |= (originalLength >> 8); + originalLength |= (originalLength >> 16); + originalLength++; + + // ensure it's a sane length + if (originalLength > 0x80000) originalLength = 0x80000; + else if (originalLength < 0x20000) originalLength = 0x20000; + + Log(LogLevel::Debug, "assuming %d\n", originalLength); + } + return originalLength; +} + +Firmware::Firmware(int consoletype) { FirmwareBufferLength = DEFAULT_FIRMWARE_LENGTH; FirmwareBuffer = new u8[FirmwareBufferLength]; @@ -184,16 +213,16 @@ SPI_Firmware::Firmware::Firmware(int consoletype) // user data header.UserSettingsOffset = (0x7FE00 & FirmwareMask) >> 3; - std::array& settings = *reinterpret_cast*>(UserDataPosition()); + std::array& settings = *reinterpret_cast*>(GetUserDataPosition()); settings = { - SPI_Firmware::UserData(), - SPI_Firmware::UserData(), + UserData(), + UserData(), }; // wifi access points // TODO: WFC ID?? - std::array& accesspoints = *reinterpret_cast*>(WifiAccessPointPosition()); + std::array& accesspoints = *reinterpret_cast*>(GetWifiAccessPointPosition()); accesspoints = { WifiAccessPoint(consoletype), @@ -203,7 +232,7 @@ SPI_Firmware::Firmware::Firmware(int consoletype) if (consoletype == 1) { - std::array& extendedaccesspoints = *reinterpret_cast*>(ExtendedAccessPointPosition()); + std::array& extendedaccesspoints = *reinterpret_cast*>(GetExtendedAccessPointPosition()); extendedaccesspoints = { ExtendedWifiAccessPoint(), @@ -213,7 +242,7 @@ SPI_Firmware::Firmware::Firmware(int consoletype) } } -SPI_Firmware::Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nullptr), FirmwareBufferLength(0), FirmwareMask(0) +Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nullptr), FirmwareBufferLength(0), FirmwareMask(0) { if (file) { @@ -239,7 +268,7 @@ SPI_Firmware::Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nu } } -SPI_Firmware::Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nullptr), FirmwareBufferLength(FixFirmwareLength(length)) +Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nullptr), FirmwareBufferLength(FixFirmwareLength(length)) { if (data) { @@ -249,14 +278,14 @@ SPI_Firmware::Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nu } } -SPI_Firmware::Firmware::Firmware(const Firmware& other) : FirmwareBuffer(nullptr), FirmwareBufferLength(other.FirmwareBufferLength) +Firmware::Firmware(const Firmware& other) : FirmwareBuffer(nullptr), FirmwareBufferLength(other.FirmwareBufferLength) { FirmwareBuffer = new u8[FirmwareBufferLength]; memcpy(FirmwareBuffer, other.FirmwareBuffer, FirmwareBufferLength); FirmwareMask = other.FirmwareMask; } -SPI_Firmware::Firmware::Firmware(Firmware&& other) noexcept +Firmware::Firmware(Firmware&& other) noexcept { FirmwareBuffer = other.FirmwareBuffer; FirmwareBufferLength = other.FirmwareBufferLength; @@ -266,7 +295,7 @@ SPI_Firmware::Firmware::Firmware(Firmware&& other) noexcept other.FirmwareMask = 0; } -SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(const Firmware& other) +Firmware& Firmware::operator=(const Firmware& other) { if (this != &other) { @@ -280,7 +309,7 @@ SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(const Firmware& other) return *this; } -SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(Firmware&& other) noexcept +Firmware& Firmware::operator=(Firmware&& other) noexcept { if (this != &other) { @@ -296,21 +325,21 @@ SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(Firmware&& other) noex return *this; } -SPI_Firmware::Firmware::~Firmware() +Firmware::~Firmware() { delete[] FirmwareBuffer; } -bool SPI_Firmware::Firmware::IsBootable() const +bool Firmware::IsBootable() const { return FirmwareBufferLength != DEFAULT_FIRMWARE_LENGTH && - Header().Identifier != GENERATED_FIRMWARE_IDENTIFIER + GetHeader().Identifier != GENERATED_FIRMWARE_IDENTIFIER ; } -const SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() const { - const std::array& userdata = UserData(); +const Firmware::UserData& Firmware::GetEffectiveUserData() const { + const std::array& userdata = GetUserData(); bool userdata0ChecksumOk = userdata[0].ChecksumValid(); bool userdata1ChecksumOk = userdata[1].ChecksumValid(); @@ -332,8 +361,8 @@ const SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() const } } -SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() { - std::array& userdata = UserData(); +Firmware::UserData& Firmware::GetEffectiveUserData() { + std::array& userdata = GetUserData(); bool userdata0ChecksumOk = userdata[0].ChecksumValid(); bool userdata1ChecksumOk = userdata[1].ChecksumValid(); @@ -355,25 +384,27 @@ SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() { } } -void SPI_Firmware::Firmware::UpdateChecksums() +void Firmware::UpdateChecksums() { - Header().UpdateChecksum(); + GetHeader().UpdateChecksum(); - for (SPI_Firmware::WifiAccessPoint& ap : AccessPoints()) + for (auto& ap : GetAccessPoints()) { ap.UpdateChecksum(); } - if (Header().ConsoleType == FirmwareConsoleType::DSi) + if (GetHeader().ConsoleType == FirmwareConsoleType::DSi) { - for (SPI_Firmware::ExtendedWifiAccessPoint& eap : ExtendedAccessPoints()) + for (auto& eap : GetExtendedAccessPoints()) { eap.UpdateChecksum(); } } - for (SPI_Firmware::UserData& u : UserData()) + for (auto& u : GetUserData()) { u.UpdateChecksum(); } +} + } \ No newline at end of file diff --git a/src/SPI_Firmware.h b/src/SPI_Firmware.h index 14771b27..c8ca25c3 100644 --- a/src/SPI_Firmware.h +++ b/src/SPI_Firmware.h @@ -24,10 +24,12 @@ #include "types.h" #include "Platform.h" -namespace SPI_Firmware +namespace melonDS { - u16 CRC16(const u8* data, u32 len, u32 start); + + + using MacAddress = std::array; using IpAddress = std::array; @@ -44,382 +46,383 @@ constexpr const char* const DEFAULT_SSID = "melonAP"; */ constexpr int EXTENDED_WIFI_SETTINGS_OFFSET = -0xA00; -enum class WepMode : u8 -{ - None = 0, - Hex5 = 1, - Hex13 = 2, - Hex16 = 3, - Ascii5 = 5, - Ascii13 = 6, - Ascii16 = 7, -}; - -enum class WpaMode : u8 -{ - Normal = 0, - WPA_WPA2 = 0x10, - WPS_WPA = 0x13, - Unused = 0xff, -}; - -enum class WpaSecurity : u8 -{ - None = 0, - WPA_TKIP = 4, - WPA2_TKIP = 5, - WPA_AES = 6, - WPA2_AES = 7, -}; - -enum class AccessPointStatus : u8 -{ - Normal = 0, - Aoss = 1, - NotConfigured = 0xff -}; - -/** - * @see https://problemkaputt.de/gbatek.htm#dsfirmwarewifiinternetaccesspoints - */ -union WifiAccessPoint -{ - /** - * Constructs an unconfigured access point. - */ - WifiAccessPoint(); - - /** - * Constructs an access point configured with melonDS's defaults. - */ - explicit WifiAccessPoint(int consoletype); - void UpdateChecksum(); - u8 Bytes[256]; - struct - { - char ProxyUsername[32]; - char ProxyPassword[32]; - char SSID[32]; - char SSIDWEP64[32]; - u8 WEPKey1[16]; - u8 WEPKey2[16]; - u8 WEPKey3[16]; - u8 WEPKey4[16]; - IpAddress Address; - IpAddress Gateway; - IpAddress PrimaryDns; - IpAddress SecondaryDns; - u8 SubnetMask; - u8 Unknown0[21]; - enum WepMode WepMode; - AccessPointStatus Status; - u8 SSIDLength; - u8 Unknown1; - u16 Mtu; - u8 Unknown2[3]; - u8 ConnectionConfigured; - u8 NintendoWFCID[6]; - u8 Unknown3[8]; - u16 Checksum; - }; -}; - -static_assert(sizeof(WifiAccessPoint) == 256, "WifiAccessPoint should be 256 bytes"); - -union ExtendedWifiAccessPoint -{ - ExtendedWifiAccessPoint(); - void UpdateChecksum(); - u8 Bytes[512]; - struct - { - WifiAccessPoint Base; - - // DSi-specific entries now - u8 PrecomputedPSK[32]; - char WPAPassword[64]; - char Unused0[33]; - WpaSecurity Security; - bool ProxyEnabled; - bool ProxyAuthentication; - char ProxyName[48]; - u8 Unused1[52]; - u16 ProxyPort; - u8 Unused2[20]; - u16 ExtendedChecksum; - } Data; -}; - -static_assert(sizeof(ExtendedWifiAccessPoint) == 512, "WifiAccessPoint should be 512 bytes"); - - -enum class FirmwareConsoleType : u8 -{ - DS = 0xFF, - DSLite = 0x20, - DSi = 0x57, - iQueDS = 0x43, - iQueDSLite = 0x63, -}; - -enum class WifiVersion : u8 -{ - V1_4 = 0, - V5 = 3, - V6_7 = 5, - W006 = 6, - W015 = 15, - W024 = 24, - N3DS = 34, -}; - -enum RFChipType : u8 -{ - Type2 = 0x2, - Type3 = 0x3, -}; - -enum class WifiBoard : u8 -{ - W015 = 0x1, - W024 = 0x2, - W028 = 0x3, - Unused = 0xff, -}; - -enum Language : u8 -{ - Japanese = 0, - English = 1, - French = 2, - German = 3, - Italian = 4, - Spanish = 5, - Chinese = 6, - Reserved = 7, -}; - -enum GBAScreen : u8 -{ - Upper = 0, - Lower = (1 << 3), -}; - -enum BacklightLevel : u8 -{ - Low = 0, - Medium = 1 << 4, - High = 2 << 4, - Max = 3 << 4 -}; - -enum BootMenu : u8 -{ - Manual = 0, - Autostart = 1 << 6, -}; - using FirmwareIdentifier = std::array; -using MacAddress = std::array; constexpr FirmwareIdentifier GENERATED_FIRMWARE_IDENTIFIER = {'M', 'E', 'L', 'N'}; -/** - * @note GBATek says the header is actually 511 bytes; - * this header struct is 512 bytes due to padding, - * but the last byte is just the first byte of the firmware's code. - * It doesn't affect the offset of any of the fields, - * so leaving that last byte in there is harmless. - * @see https://problemkaputt.de/gbatek.htm#dsfirmwareheader - * @see https://problemkaputt.de/gbatek.htm#dsfirmwarewificalibrationdata -*/ -union FirmwareHeader -{ - explicit FirmwareHeader(int consoletype); - void UpdateChecksum(); - u8 Bytes[512]; - struct - { - u16 ARM9GUICodeOffset; - u16 ARM7WifiCodeOffset; - u16 GUIWifiCodeChecksum; - u16 BootCodeChecksum; - - FirmwareIdentifier Identifier; - - u16 ARM9BootCodeROMAddress; - u16 ARM9BootCodeRAMAddress; - u16 ARM7BootCodeRAMAddress; - u16 ARM7BootCodeROMAddress; - u16 ShiftAmounts; - u16 DataGfxRomAddress; - - u8 BuildMinute; - u8 BuildHour; - u8 BuildDay; - u8 BuildMonth; - u8 BuildYear; - - FirmwareConsoleType ConsoleType; - - u8 Unused0[2]; - - u16 UserSettingsOffset; - u8 Unknown0[2]; - u8 Unknown1[2]; - u16 DataGfxChecksum; - u8 Unused2[2]; - - // Begin wi-fi settings - u16 WifiConfigChecksum; - u16 WifiConfigLength; - u8 Unused1; - enum WifiVersion WifiVersion; - - u8 Unused3[6]; - - SPI_Firmware::MacAddress MacAddress; - - u16 EnabledChannels; - - u8 Unknown2[2]; - - enum RFChipType RFChipType; - u8 RFBitsPerEntry; - u8 RFEntries; - u8 Unknown3; - - u8 InitialValues[32]; - u8 InitialBBValues[105]; - u8 Unused4; - union - { - struct - { - u8 InitialRFValues[36]; - u8 InitialRF56Values[84]; - u8 InitialBB1EValues[14]; - u8 InitialRf9Values[14]; - } Type2Config; - - struct - { - u8 InitialRFValues[41]; - u8 BBIndicesPerChannel; - u8 BBIndex1; - u8 BBData1[14]; - u8 BBIndex2; - u8 BBData2[14]; - u8 RFIndex1; - u8 RFData1[14]; - u8 RFIndex2; - u8 RFData2[14]; - u8 Unused0[46]; - } Type3Config; - }; - - u8 Unknown4; - u8 Unused5; - u8 Unused6[153]; - enum WifiBoard WifiBoard; - u8 WifiFlash; - u8 Unused7; - }; -}; - -static_assert(sizeof(FirmwareHeader) == 512, "FirmwareHeader should be 512 bytes"); - -struct ExtendedUserSettings -{ - char ID[8]; - u16 Checksum; - u16 ChecksumLength; - u8 Version; - u8 UpdateCount; - u8 BootMenuFlags; - u8 GBABorder; - u16 TemperatureCalibration0; - u16 TemperatureCalibration1; - u16 TemperatureCalibrationDegrees; - u8 TemperatureFlags; - u8 BacklightIntensity; - u32 DateCenturyOffset; - u8 DateMonthRecovery; - u8 DateDayRecovery; - u8 DateYearRecovery; - u8 DateTimeFlags; - char DateSeparator; - char TimeSeparator; - char DecimalSeparator; - char ThousandsSeparator; - u8 DaylightSavingsTimeNth; - u8 DaylightSavingsTimeDay; - u8 DaylightSavingsTimeOfMonth; - u8 DaylightSavingsTimeFlags; -}; - -static_assert(sizeof(ExtendedUserSettings) == 0x28, "ExtendedUserSettings should be 40 bytes"); - -union UserData -{ - UserData(); - void UpdateChecksum(); - [[nodiscard]] bool ChecksumValid() const - { - bool baseChecksumOk = Checksum == CRC16(Bytes, 0x70, 0xFFFF); - bool extendedChecksumOk = Bytes[0x74] != 1 || ExtendedSettings.Checksum == CRC16(Bytes + 0x74, 0x8A, 0xFFFF); - // For our purposes, the extended checksum is OK if we're not using extended data - - return baseChecksumOk && extendedChecksumOk; - } - - u8 Bytes[256]; - struct - { - u16 Version; - u8 FavoriteColor; - u8 BirthdayMonth; - u8 BirthdayDay; - u8 Unused0; - char16_t Nickname[10]; - u16 NameLength; - char16_t Message[26]; - u16 MessageLength; - u8 AlarmHour; - u8 AlarmMinute; - u8 Unknown0[2]; - u8 AlarmFlags; - u8 Unused1; - u16 TouchCalibrationADC1[2]; - u8 TouchCalibrationPixel1[2]; - u16 TouchCalibrationADC2[2]; - u8 TouchCalibrationPixel2[2]; - u16 Settings; - u8 Year; - u8 Unknown1; - u32 RTCOffset; - u8 Unused2[4]; - u16 UpdateCounter; - u16 Checksum; - union - { - u8 Unused3[0x8C]; - struct - { - u8 Unknown0; - Language ExtendedLanguage; // padded - u16 SupportedLanguageMask; - u8 Unused0[0x86]; - u16 Checksum; - } ExtendedSettings; - }; - }; -}; -static_assert(sizeof(UserData) == 256, "UserData should be 256 bytes"); class Firmware { public: + + enum class WepMode : u8 + { + None = 0, + Hex5 = 1, + Hex13 = 2, + Hex16 = 3, + Ascii5 = 5, + Ascii13 = 6, + Ascii16 = 7, + }; + + enum class WpaMode : u8 + { + Normal = 0, + WPA_WPA2 = 0x10, + WPS_WPA = 0x13, + Unused = 0xff, + }; + + enum class WpaSecurity : u8 + { + None = 0, + WPA_TKIP = 4, + WPA2_TKIP = 5, + WPA_AES = 6, + WPA2_AES = 7, + }; + + enum class AccessPointStatus : u8 + { + Normal = 0, + Aoss = 1, + NotConfigured = 0xff + }; + + /** + * @see https://problemkaputt.de/gbatek.htm#dsfirmwarewifiinternetaccesspoints + */ + union WifiAccessPoint + { + /** + * Constructs an unconfigured access point. + */ + WifiAccessPoint(); + + /** + * Constructs an access point configured with melonDS's defaults. + */ + explicit WifiAccessPoint(int consoletype); + void UpdateChecksum(); + u8 Bytes[256]; + struct + { + char ProxyUsername[32]; + char ProxyPassword[32]; + char SSID[32]; + char SSIDWEP64[32]; + u8 WEPKey1[16]; + u8 WEPKey2[16]; + u8 WEPKey3[16]; + u8 WEPKey4[16]; + IpAddress Address; + IpAddress Gateway; + IpAddress PrimaryDns; + IpAddress SecondaryDns; + u8 SubnetMask; + u8 Unknown0[21]; + enum WepMode WepMode; + AccessPointStatus Status; + u8 SSIDLength; + u8 Unknown1; + u16 Mtu; + u8 Unknown2[3]; + u8 ConnectionConfigured; + u8 NintendoWFCID[6]; + u8 Unknown3[8]; + u16 Checksum; + }; + }; + + static_assert(sizeof(WifiAccessPoint) == 256, "WifiAccessPoint should be 256 bytes"); + + union ExtendedWifiAccessPoint + { + ExtendedWifiAccessPoint(); + void UpdateChecksum(); + u8 Bytes[512]; + struct + { + WifiAccessPoint Base; + + // DSi-specific entries now + u8 PrecomputedPSK[32]; + char WPAPassword[64]; + char Unused0[33]; + WpaSecurity Security; + bool ProxyEnabled; + bool ProxyAuthentication; + char ProxyName[48]; + u8 Unused1[52]; + u16 ProxyPort; + u8 Unused2[20]; + u16 ExtendedChecksum; + } Data; + }; + + static_assert(sizeof(ExtendedWifiAccessPoint) == 512, "WifiAccessPoint should be 512 bytes"); + + + enum class FirmwareConsoleType : u8 + { + DS = 0xFF, + DSLite = 0x20, + DSi = 0x57, + iQueDS = 0x43, + iQueDSLite = 0x63, + }; + + enum class WifiVersion : u8 + { + V1_4 = 0, + V5 = 3, + V6_7 = 5, + W006 = 6, + W015 = 15, + W024 = 24, + N3DS = 34, + }; + + enum RFChipType : u8 + { + Type2 = 0x2, + Type3 = 0x3, + }; + + enum class WifiBoard : u8 + { + W015 = 0x1, + W024 = 0x2, + W028 = 0x3, + Unused = 0xff, + }; + + enum Language : u8 + { + Japanese = 0, + English = 1, + French = 2, + German = 3, + Italian = 4, + Spanish = 5, + Chinese = 6, + Reserved = 7, + }; + + enum GBAScreen : u8 + { + Upper = 0, + Lower = (1 << 3), + }; + + enum BacklightLevel : u8 + { + Low = 0, + Medium = 1 << 4, + High = 2 << 4, + Max = 3 << 4 + }; + + enum BootMenu : u8 + { + Manual = 0, + Autostart = 1 << 6, + }; + + /** + * @note GBATek says the header is actually 511 bytes; + * this header struct is 512 bytes due to padding, + * but the last byte is just the first byte of the firmware's code. + * It doesn't affect the offset of any of the fields, + * so leaving that last byte in there is harmless. + * @see https://problemkaputt.de/gbatek.htm#dsfirmwareheader + * @see https://problemkaputt.de/gbatek.htm#dsfirmwarewificalibrationdata + */ + union FirmwareHeader + { + explicit FirmwareHeader(int consoletype); + void UpdateChecksum(); + u8 Bytes[512]; + struct + { + u16 ARM9GUICodeOffset; + u16 ARM7WifiCodeOffset; + u16 GUIWifiCodeChecksum; + u16 BootCodeChecksum; + + FirmwareIdentifier Identifier; + + u16 ARM9BootCodeROMAddress; + u16 ARM9BootCodeRAMAddress; + u16 ARM7BootCodeRAMAddress; + u16 ARM7BootCodeROMAddress; + u16 ShiftAmounts; + u16 DataGfxRomAddress; + + u8 BuildMinute; + u8 BuildHour; + u8 BuildDay; + u8 BuildMonth; + u8 BuildYear; + + FirmwareConsoleType ConsoleType; + + u8 Unused0[2]; + + u16 UserSettingsOffset; + u8 Unknown0[2]; + u8 Unknown1[2]; + u16 DataGfxChecksum; + u8 Unused2[2]; + + // Begin wi-fi settings + u16 WifiConfigChecksum; + u16 WifiConfigLength; + u8 Unused1; + enum WifiVersion WifiVersion; + + u8 Unused3[6]; + + MacAddress MacAddr; + + u16 EnabledChannels; + + u8 Unknown2[2]; + + enum RFChipType RFChipType; + u8 RFBitsPerEntry; + u8 RFEntries; + u8 Unknown3; + + u8 InitialValues[32]; + u8 InitialBBValues[105]; + u8 Unused4; + union + { + struct + { + u8 InitialRFValues[36]; + u8 InitialRF56Values[84]; + u8 InitialBB1EValues[14]; + u8 InitialRf9Values[14]; + } Type2Config; + + struct + { + u8 InitialRFValues[41]; + u8 BBIndicesPerChannel; + u8 BBIndex1; + u8 BBData1[14]; + u8 BBIndex2; + u8 BBData2[14]; + u8 RFIndex1; + u8 RFData1[14]; + u8 RFIndex2; + u8 RFData2[14]; + u8 Unused0[46]; + } Type3Config; + }; + + u8 Unknown4; + u8 Unused5; + u8 Unused6[153]; + enum WifiBoard WifiBoard; + u8 WifiFlash; + u8 Unused7; + }; + }; + + static_assert(sizeof(FirmwareHeader) == 512, "FirmwareHeader should be 512 bytes"); + + struct ExtendedUserSettings + { + char ID[8]; + u16 Checksum; + u16 ChecksumLength; + u8 Version; + u8 UpdateCount; + u8 BootMenuFlags; + u8 GBABorder; + u16 TemperatureCalibration0; + u16 TemperatureCalibration1; + u16 TemperatureCalibrationDegrees; + u8 TemperatureFlags; + u8 BacklightIntensity; + u32 DateCenturyOffset; + u8 DateMonthRecovery; + u8 DateDayRecovery; + u8 DateYearRecovery; + u8 DateTimeFlags; + char DateSeparator; + char TimeSeparator; + char DecimalSeparator; + char ThousandsSeparator; + u8 DaylightSavingsTimeNth; + u8 DaylightSavingsTimeDay; + u8 DaylightSavingsTimeOfMonth; + u8 DaylightSavingsTimeFlags; + }; + + static_assert(sizeof(ExtendedUserSettings) == 0x28, "ExtendedUserSettings should be 40 bytes"); + + union UserData + { + UserData(); + void UpdateChecksum(); + [[nodiscard]] bool ChecksumValid() const + { + bool baseChecksumOk = Checksum == CRC16(Bytes, 0x70, 0xFFFF); + bool extendedChecksumOk = Bytes[0x74] != 1 || ExtendedSettings.Checksum == CRC16(Bytes + 0x74, 0x8A, 0xFFFF); + // For our purposes, the extended checksum is OK if we're not using extended data + + return baseChecksumOk && extendedChecksumOk; + } + + u8 Bytes[256]; + struct + { + u16 Version; + u8 FavoriteColor; + u8 BirthdayMonth; + u8 BirthdayDay; + u8 Unused0; + char16_t Nickname[10]; + u16 NameLength; + char16_t Message[26]; + u16 MessageLength; + u8 AlarmHour; + u8 AlarmMinute; + u8 Unknown0[2]; + u8 AlarmFlags; + u8 Unused1; + u16 TouchCalibrationADC1[2]; + u8 TouchCalibrationPixel1[2]; + u16 TouchCalibrationADC2[2]; + u8 TouchCalibrationPixel2[2]; + u16 Settings; + u8 Year; + u8 RTCClockAdjust; + u32 RTCOffset; + u8 Unused2[4]; + u16 UpdateCounter; + u16 Checksum; + union + { + u8 Unused3[0x8C]; + struct + { + u8 Unknown0; + Language ExtendedLanguage; // padded + u16 SupportedLanguageMask; + u8 Unused0[0x86]; + u16 Checksum; + } ExtendedSettings; + }; + }; + }; + static_assert(sizeof(UserData) == 256, "UserData should be 256 bytes"); + /** * Constructs a default firmware blob * filled with data necessary for booting and configuring NDS games. @@ -449,28 +452,28 @@ public: Firmware& operator=(Firmware&& other) noexcept; ~Firmware(); - [[nodiscard]] FirmwareHeader& Header() { return *reinterpret_cast(FirmwareBuffer); } - [[nodiscard]] const FirmwareHeader& Header() const { return *reinterpret_cast(FirmwareBuffer); } + [[nodiscard]] FirmwareHeader& GetHeader() { return *reinterpret_cast(FirmwareBuffer); } + [[nodiscard]] const FirmwareHeader& GetHeader() const { return *reinterpret_cast(FirmwareBuffer); } /// @return The offset of the first basic Wi-fi settings block in the firmware /// (not the extended Wi-fi settings block used by the DSi). /// @see WifiAccessPointPosition - [[nodiscard]] u32 WifiAccessPointOffset() const { return UserDataOffset() - 0x400; } + [[nodiscard]] u32 GetWifiAccessPointOffset() const { return GetUserDataOffset() - 0x400; } /// @return The address of the first basic Wi-fi settings block in the firmware. - [[nodiscard]] u8* WifiAccessPointPosition() { return FirmwareBuffer + WifiAccessPointOffset(); } - [[nodiscard]] const u8* WifiAccessPointPosition() const { return FirmwareBuffer + WifiAccessPointOffset(); } + [[nodiscard]] u8* GetWifiAccessPointPosition() { return FirmwareBuffer + GetWifiAccessPointOffset(); } + [[nodiscard]] const u8* GetWifiAccessPointPosition() const { return FirmwareBuffer + GetWifiAccessPointOffset(); } - [[nodiscard]] const std::array& AccessPoints() const + [[nodiscard]] const std::array& GetAccessPoints() const { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(WifiAccessPointPosition()); + return *reinterpret_cast*>(GetWifiAccessPointPosition()); } - [[nodiscard]] std::array& AccessPoints() + [[nodiscard]] std::array& GetAccessPoints() { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(WifiAccessPointPosition()); + return *reinterpret_cast*>(GetWifiAccessPointPosition()); } /// @returns \c true if this firmware image contains bootable code. @@ -481,20 +484,20 @@ public: /// @return The address of the first extended Wi-fi settings block in the firmware. /// @warning Only meaningful if this is DSi firmware. - [[nodiscard]] u32 ExtendedAccessPointOffset() const { return UserDataOffset() + EXTENDED_WIFI_SETTINGS_OFFSET; } - [[nodiscard]] u8* ExtendedAccessPointPosition() { return FirmwareBuffer + ExtendedAccessPointOffset(); } - [[nodiscard]] const u8* ExtendedAccessPointPosition() const { return FirmwareBuffer + ExtendedAccessPointOffset(); } + [[nodiscard]] u32 GetExtendedAccessPointOffset() const { return GetUserDataOffset() + EXTENDED_WIFI_SETTINGS_OFFSET; } + [[nodiscard]] u8* GetExtendedAccessPointPosition() { return FirmwareBuffer + GetExtendedAccessPointOffset(); } + [[nodiscard]] const u8* GetExtendedAccessPointPosition() const { return FirmwareBuffer + GetExtendedAccessPointOffset(); } - [[nodiscard]] const std::array& ExtendedAccessPoints() const + [[nodiscard]] const std::array& GetExtendedAccessPoints() const { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(ExtendedAccessPointPosition()); + return *reinterpret_cast*>(GetExtendedAccessPointPosition()); } - [[nodiscard]] std::array& ExtendedAccessPoints() + [[nodiscard]] std::array& GetExtendedAccessPoints() { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(ExtendedAccessPointPosition()); + return *reinterpret_cast*>(GetExtendedAccessPointPosition()); } /// @return The pointer to the firmware buffer, @@ -508,21 +511,21 @@ public: /// @return The offset of the first user data section in the firmware. /// @see UserDataPosition - [[nodiscard]] u32 UserDataOffset() const { return Header().UserSettingsOffset << 3; } + [[nodiscard]] u32 GetUserDataOffset() const { return GetHeader().UserSettingsOffset << 3; } /// @return The address of the first user data section in the firmware. /// @see UserDataOffset - [[nodiscard]] u8* UserDataPosition() { return FirmwareBuffer + UserDataOffset(); } - [[nodiscard]] const u8* UserDataPosition() const { return FirmwareBuffer + UserDataOffset(); } + [[nodiscard]] u8* GetUserDataPosition() { return FirmwareBuffer + GetUserDataOffset(); } + [[nodiscard]] const u8* GetUserDataPosition() const { return FirmwareBuffer + GetUserDataOffset(); } /// @return Reference to the two user data sections. /// @note Either \c UserData object could be the "effective" one, /// so prefer using \c EffectiveUserData() if you're not modifying both. - [[nodiscard]] const std::array& UserData() const + [[nodiscard]] const std::array& GetUserData() const { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(UserDataPosition()); + return *reinterpret_cast*>(GetUserDataPosition()); }; /** @@ -531,10 +534,10 @@ public: * so prefer using \c EffectiveUserData() if you're not modifying both. * @warning Remember to call UserData::UpdateChecksum() after modifying any of its fields. */ - [[nodiscard]] std::array& UserData() + [[nodiscard]] std::array& GetUserData() { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(UserDataPosition()); + return *reinterpret_cast*>(GetUserDataPosition()); } /** @@ -543,13 +546,16 @@ public: * Specifically, the firmware will use whichever one has the valid checksum * (or the newer one if they're both valid). */ - [[nodiscard]] const union UserData& EffectiveUserData() const; + [[nodiscard]] const union UserData& GetEffectiveUserData() const; /** * @return Reference to whichever of the two user data sections * has the highest update counter. */ - [[nodiscard]] union UserData& EffectiveUserData(); + [[nodiscard]] union UserData& GetEffectiveUserData(); + + /// Fix the given firmware length to an acceptable length + u32 FixFirmwareLength(u32 originalLength); /// Updates the checksums of all used sections of the firmware. void UpdateChecksums(); @@ -558,6 +564,6 @@ private: u32 FirmwareBufferLength; u32 FirmwareMask; }; -} +} #endif //MELONDS_SPI_FIRMWARE_H diff --git a/src/SPU.cpp b/src/SPU.cpp index e83a0c71..69c0b9de 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,6 +24,8 @@ #include "DSi.h" #include "SPU.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -32,12 +34,10 @@ using Platform::LogLevel; // * capture addition modes, overflow bugs // * channel hold -namespace SPU -{ -const s8 ADPCMIndexTable[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; +const s8 SPUChannel::ADPCMIndexTable[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; -const u16 ADPCMTable[89] = +const u16 SPUChannel::ADPCMTable[89] = { 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, @@ -53,7 +53,7 @@ const u16 ADPCMTable[89] = 0x7FFF }; -const s16 PSGTable[8][8] = +const s16 SPUChannel::PSGTable[8][8] = { {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF}, {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF}, @@ -65,91 +65,164 @@ const s16 PSGTable[8][8] = {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF} }; -// audio interpolation is an improvement upon the original hardware -// (which performs no interpolation) -int InterpType; -s16 InterpCos[0x100]; -s16 InterpCubic[0x100][4]; - -const u32 OutputBufferSize = 2*2048; -s16 OutputBackbuffer[2 * OutputBufferSize]; -u32 OutputBackbufferWritePosition; - -s16 OutputFrontBuffer[2 * OutputBufferSize]; -u32 OutputFrontBufferWritePosition; -u32 OutputFrontBufferReadPosition; - -Platform::Mutex* AudioLock; - -u16 Cnt; -u8 MasterVolume; -u16 Bias; -bool ApplyBias; -bool Degrade10Bit; - -Channel* Channels[16]; -CaptureUnit* Capture[2]; - - -bool Init() +template +constexpr T ipow(T num, unsigned int pow) { - for (int i = 0; i < 16; i++) - Channels[i] = new Channel(i); + T product = 1; + for (int i = 0; i < pow; ++i) + { + product *= num; + } - Capture[0] = new CaptureUnit(0); - Capture[1] = new CaptureUnit(1); + return product; +} - AudioLock = Platform::Mutex_Create(); +template +constexpr T factorial(T num) +{ + T product = 1; + for (T i = 1; i <= num; ++i) + { + product *= i; + } - InterpType = 0; - ApplyBias = true; - Degrade10Bit = false; + return product; +} - // generate interpolation tables - // values are 1:1:14 fixed-point +// We can't use std::cos in constexpr functions until C++26, +// so we need to compute the cosine ourselves with the Taylor series. +// Code adapted from https://prosepoetrycode.potterpcs.net/2015/07/a-simple-constexpr-power-function-c/ +template +constexpr double cosine (double theta) +{ + return (ipow(-1, Iterations) * ipow(theta, 2 * Iterations)) / + static_cast(factorial(2ull * Iterations)) + + cosine(theta); +} + +template <> +constexpr double cosine<0> (double theta) +{ + return 1.0; +} + +// generate interpolation tables +// values are 1:1:14 fixed-point +constexpr std::array InterpCos = []() constexpr { + std::array interp {}; - float m_pi = std::acos(-1.0f); for (int i = 0; i < 0x100; i++) { - float ratio = (i * m_pi) / 255.0f; - ratio = 1.0f - std::cos(ratio); + float ratio = (i * M_PI) / 255.0f; + ratio = 1.0f - cosine(ratio); - InterpCos[i] = (s16)(ratio * 0x2000); + interp[i] = (s16)(ratio * 0x2000); } + return interp; +}(); + +constexpr array2d InterpCubic = []() constexpr { + array2d interp {}; + for (int i = 0; i < 0x100; i++) { s32 i1 = i << 6; s32 i2 = (i * i) >> 2; s32 i3 = (i * i * i) >> 10; - InterpCubic[i][0] = -i3 + 2*i2 - i1; - InterpCubic[i][1] = i3 - 2*i2 + 0x4000; - InterpCubic[i][2] = -i3 + i2 + i1; - InterpCubic[i][3] = i3 - i2; + interp[i][0] = -i3 + 2*i2 - i1; + interp[i][1] = i3 - 2*i2 + 0x4000; + interp[i][2] = -i3 + i2 + i1; + interp[i][3] = i3 - i2; } - return true; + return interp; +}(); + +const std::array InterpSNESGauss = { + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, + 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, 0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A, + 0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E, 0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011, + 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, 0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B, + 0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021, 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028, + 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, + 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042, 0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D, + 0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, 0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066, + 0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075, 0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084, + 0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096, 0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8, + 0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC, 0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2, + 0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9, 0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101, + 0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B, 0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137, + 0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153, 0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172, + 0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191, 0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2, + 0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5, 0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8, + 0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C, 0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241, + 0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267, 0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E, + 0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5, 0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC, + 0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304, 0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B, + 0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353, 0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379, + 0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F, 0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5, + 0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9, 0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C, + 0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E, 0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E, + 0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C, 0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488, + 0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2, 0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA, + 0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0, 0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3, + 0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3, 0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500, + 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B, 0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512, + 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 +}; + +SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation) : + NDS(nds), + Channels { + SPUChannel(0, nds, interpolation), + SPUChannel(1, nds, interpolation), + SPUChannel(2, nds, interpolation), + SPUChannel(3, nds, interpolation), + SPUChannel(4, nds, interpolation), + SPUChannel(5, nds, interpolation), + SPUChannel(6, nds, interpolation), + SPUChannel(7, nds, interpolation), + SPUChannel(8, nds, interpolation), + SPUChannel(9, nds, interpolation), + SPUChannel(10, nds, interpolation), + SPUChannel(11, nds, interpolation), + SPUChannel(12, nds, interpolation), + SPUChannel(13, nds, interpolation), + SPUChannel(14, nds, interpolation), + SPUChannel(15, nds, interpolation), + }, + Capture { + SPUCaptureUnit(0, nds), + SPUCaptureUnit(1, nds), + }, + AudioLock(Platform::Mutex_Create()), + Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto)) +{ + NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix)); + + ApplyBias = true; + Degrade10Bit = false; + + memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); + + OutputBackbufferWritePosition = 0; + OutputFrontBufferReadPosition = 0; + OutputFrontBufferWritePosition = 0; } -void DeInit() +SPU::~SPU() { - for (int i = 0; i < 16; i++) - { - delete Channels[i]; - Channels[i] = nullptr; - } - - delete Capture[0]; - delete Capture[1]; - Capture[0] = nullptr; - Capture[1] = nullptr; - Platform::Mutex_Free(AudioLock); AudioLock = nullptr; + + NDS.UnregisterEventFunc(Event_SPU, 0); } -void Reset() +void SPU::Reset() { InitOutput(); @@ -158,15 +231,15 @@ void Reset() Bias = 0; for (int i = 0; i < 16; i++) - Channels[i]->Reset(); + Channels[i].Reset(); - Capture[0]->Reset(); - Capture[1]->Reset(); + Capture[0].Reset(); + Capture[1].Reset(); - NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0); + NDS.ScheduleEvent(Event_SPU, false, 1024, 0, 0); } -void Stop() +void SPU::Stop() { Platform::Mutex_Lock(AudioLock); memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); @@ -177,7 +250,7 @@ void Stop() Platform::Mutex_Unlock(AudioLock); } -void DoSavestate(Savestate* file) +void SPU::DoSavestate(Savestate* file) { file->Section("SPU."); @@ -185,57 +258,66 @@ void DoSavestate(Savestate* file) file->Var8(&MasterVolume); file->Var16(&Bias); - for (int i = 0; i < 16; i++) - Channels[i]->DoSavestate(file); + for (SPUChannel& channel : Channels) + channel.DoSavestate(file); - Capture[0]->DoSavestate(file); - Capture[1]->DoSavestate(file); + for (SPUCaptureUnit& capture : Capture) + capture.DoSavestate(file); } -void SetPowerCnt(u32 val) +void SPU::SetPowerCnt(u32 val) { // TODO } -void SetInterpolation(int type) +void SPU::SetInterpolation(AudioInterpolation type) { - InterpType = type; + for (SPUChannel& channel : Channels) + channel.InterpType = type; } -void SetBias(u16 bias) +void SPU::SetBias(u16 bias) { Bias = bias; } -void SetApplyBias(bool enable) +void SPU::SetApplyBias(bool enable) { ApplyBias = enable; } -void SetDegrade10Bit(bool enable) +void SPU::SetDegrade10Bit(bool enable) { Degrade10Bit = enable; } - -Channel::Channel(u32 num) +void SPU::SetDegrade10Bit(AudioBitDepth depth) { - Num = num; + switch (depth) + { + case AudioBitDepth::Auto: + Degrade10Bit = (NDS.ConsoleType == 0); + break; + case AudioBitDepth::_10Bit: + Degrade10Bit = true; + break; + case AudioBitDepth::_16Bit: + Degrade10Bit = false; + break; + } } -Channel::~Channel() +SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds, AudioInterpolation interpolation) : + NDS(nds), + Num(num), + InterpType(interpolation) { } -void Channel::Reset() +void SPUChannel::Reset() { - if (NDS::ConsoleType == 1) - BusRead32 = DSi::ARM7Read32; - else - BusRead32 = NDS::ARM7Read32; - KeyOn = false; SetCnt(0); @@ -253,7 +335,7 @@ void Channel::Reset() FIFOLevel = 0; } -void Channel::DoSavestate(Savestate* file) +void SPUChannel::DoSavestate(Savestate* file) { file->Var32(&Cnt); file->Var32(&SrcAddr); @@ -285,7 +367,7 @@ void Channel::DoSavestate(Savestate* file) file->VarArray(FIFO, sizeof(FIFO)); } -void Channel::FIFO_BufferData() +void SPUChannel::FIFO_BufferData() { u32 totallen = LoopPos + Length; @@ -305,7 +387,7 @@ void Channel::FIFO_BufferData() { for (u32 i = 0; i < burstlen; i += 4) { - FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset); + FIFO[FIFOWritePos] = NDS.ARM7Read32(SrcAddr + FIFOReadOffset); FIFOReadOffset += 4; FIFOWritePos++; FIFOWritePos &= 0x7; @@ -326,7 +408,7 @@ void Channel::FIFO_BufferData() } template -T Channel::FIFO_ReadData() +T SPUChannel::FIFO_ReadData() { T ret = *(T*)&((u8*)FIFO)[FIFOReadPos]; @@ -340,7 +422,7 @@ T Channel::FIFO_ReadData() return ret; } -void Channel::Start() +void SPUChannel::Start() { Timer = TimerReload; @@ -368,7 +450,7 @@ void Channel::Start() } } -void Channel::NextSample_PCM8() +void SPUChannel::NextSample_PCM8() { Pos++; if (Pos < 0) return; @@ -391,7 +473,7 @@ void Channel::NextSample_PCM8() CurSample = val << 8; } -void Channel::NextSample_PCM16() +void SPUChannel::NextSample_PCM16() { Pos++; if (Pos < 0) return; @@ -414,7 +496,7 @@ void Channel::NextSample_PCM16() CurSample = val; } -void Channel::NextSample_ADPCM() +void SPUChannel::NextSample_ADPCM() { Pos++; if (Pos < 8) @@ -489,13 +571,13 @@ void Channel::NextSample_ADPCM() CurSample = ADPCMVal; } -void Channel::NextSample_PSG() +void SPUChannel::NextSample_PSG() { Pos++; CurSample = PSGTable[(Cnt >> 24) & 0x7][Pos & 0x7]; } -void Channel::NextSample_Noise() +void SPUChannel::NextSample_Noise() { if (NoiseVal & 0x1) { @@ -510,7 +592,7 @@ void Channel::NextSample_Noise() } template -s32 Channel::Run() +s32 SPUChannel::Run() { if (!(Cnt & (1<<31))) return 0; @@ -531,7 +613,7 @@ s32 Channel::Run() // for optional interpolation: save previous samples // the interpolated audio will be delayed by a couple samples, // but it's easier to deal with this way - if ((type < 3) && (InterpType != 0)) + if ((type < 3) && (InterpType != AudioInterpolation::None)) { PrevSample[2] = PrevSample[1]; PrevSample[1] = PrevSample[0]; @@ -551,29 +633,44 @@ s32 Channel::Run() s32 val = (s32)CurSample; // interpolation (emulation improvement, not a hardware feature) - if ((type < 3) && (InterpType != 0)) + if ((type < 3) && (InterpType != AudioInterpolation::None)) { s32 samplepos = ((Timer - TimerReload) * 0x100) / (0x10000 - TimerReload); if (samplepos > 0xFF) samplepos = 0xFF; switch (InterpType) { - case 1: // linear + case AudioInterpolation::Linear: val = ((val * samplepos) + (PrevSample[0] * (0xFF-samplepos))) >> 8; break; - case 2: // cosine + case AudioInterpolation::Cosine: val = ((val * InterpCos[samplepos]) + (PrevSample[0] * InterpCos[0xFF-samplepos])) >> 14; break; - case 3: // cubic + case AudioInterpolation::Cubic: val = ((PrevSample[2] * InterpCubic[samplepos][0]) + (PrevSample[1] * InterpCubic[samplepos][1]) + (PrevSample[0] * InterpCubic[samplepos][2]) + (val * InterpCubic[samplepos][3])) >> 14; break; + + case AudioInterpolation::SNESGaussian: { + // Avoid clipping (from fullsnes) +#define CLAMP(s) (std::clamp((s) >> 1, -0x3FFA, 0x3FF8)) + s32 out = (InterpSNESGauss[0x0FF - samplepos] * CLAMP(PrevSample[2]) >> 10); + out = out + ((InterpSNESGauss[0x1FF - samplepos] * CLAMP(PrevSample[1])) >> 10); + out = out + ((InterpSNESGauss[0x100 + samplepos] * CLAMP(PrevSample[0])) >> 10); + out = out + ((InterpSNESGauss[0x000 + samplepos] * CLAMP(val)) >> 10); + val = std::clamp(out, -0x8000, 0x7FFF); +#undef CLAMP + break; + } + + default: + break; } } @@ -582,29 +679,19 @@ s32 Channel::Run() return val; } -void Channel::PanOutput(s32 in, s32& left, s32& right) +void SPUChannel::PanOutput(s32 in, s32& left, s32& right) { left += ((s64)in * (128-Pan)) >> 10; right += ((s64)in * Pan) >> 10; } -CaptureUnit::CaptureUnit(u32 num) -{ - Num = num; -} - -CaptureUnit::~CaptureUnit() +SPUCaptureUnit::SPUCaptureUnit(u32 num, melonDS::NDS& nds) : NDS(nds), Num(num) { } -void CaptureUnit::Reset() +void SPUCaptureUnit::Reset() { - if (NDS::ConsoleType == 1) - BusWrite32 = DSi::ARM7Write32; - else - BusWrite32 = NDS::ARM7Write32; - SetCnt(0); DstAddr = 0; TimerReload = 0; @@ -619,7 +706,7 @@ void CaptureUnit::Reset() FIFOLevel = 0; } -void CaptureUnit::DoSavestate(Savestate* file) +void SPUCaptureUnit::DoSavestate(Savestate* file) { file->Var8(&Cnt); file->Var32(&DstAddr); @@ -636,11 +723,12 @@ void CaptureUnit::DoSavestate(Savestate* file) file->VarArray(FIFO, 4*4); } -void CaptureUnit::FIFO_FlushData() +void SPUCaptureUnit::FIFO_FlushData() { for (u32 i = 0; i < 4; i++) { - BusWrite32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]); + NDS.ARM7Write32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]); + // Calls the NDS or DSi version, depending on the class FIFOReadPos++; FIFOReadPos &= 0x3; @@ -656,7 +744,7 @@ void CaptureUnit::FIFO_FlushData() } template -void CaptureUnit::FIFO_WriteData(T val) +void SPUCaptureUnit::FIFO_WriteData(T val) { *(T*)&((u8*)FIFO)[FIFOWritePos] = val; @@ -668,7 +756,7 @@ void CaptureUnit::FIFO_WriteData(T val) FIFO_FlushData(); } -void CaptureUnit::Run(s32 sample) +void SPUCaptureUnit::Run(s32 sample) { Timer += 512; @@ -721,28 +809,28 @@ void CaptureUnit::Run(s32 sample) } -void Mix(u32 dummy) +void SPU::Mix(u32 dummy) { s32 left = 0, right = 0; s32 leftoutput = 0, rightoutput = 0; - if (Cnt & (1<<15)) + if ((Cnt & (1<<15)) && (!dummy)) { - s32 ch0 = Channels[0]->DoRun(); - s32 ch1 = Channels[1]->DoRun(); - s32 ch2 = Channels[2]->DoRun(); - s32 ch3 = Channels[3]->DoRun(); + s32 ch0 = Channels[0].DoRun(); + s32 ch1 = Channels[1].DoRun(); + s32 ch2 = Channels[2].DoRun(); + s32 ch3 = Channels[3].DoRun(); // TODO: addition from capture registers - Channels[0]->PanOutput(ch0, left, right); - Channels[2]->PanOutput(ch2, left, right); + Channels[0].PanOutput(ch0, left, right); + Channels[2].PanOutput(ch2, left, right); - if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1, left, right); - if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3, left, right); + if (!(Cnt & (1<<12))) Channels[1].PanOutput(ch1, left, right); + if (!(Cnt & (1<<13))) Channels[3].PanOutput(ch3, left, right); for (int i = 4; i < 16; i++) { - Channel* chan = Channels[i]; + SPUChannel* chan = &Channels[i]; s32 channel = chan->DoRun(); chan->PanOutput(channel, left, right); @@ -751,7 +839,7 @@ void Mix(u32 dummy) // sound capture // TODO: other sound capture sources, along with their bugs - if (Capture[0]->Cnt & (1<<7)) + if (Capture[0].Cnt & (1<<7)) { s32 val = left; @@ -759,10 +847,10 @@ void Mix(u32 dummy) if (val < -0x8000) val = -0x8000; else if (val > 0x7FFF) val = 0x7FFF; - Capture[0]->Run(val); + Capture[0].Run(val); } - if (Capture[1]->Cnt & (1<<7)) + if (Capture[1].Cnt & (1<<7)) { s32 val = right; @@ -770,7 +858,7 @@ void Mix(u32 dummy) if (val < -0x8000) val = -0x8000; else if (val > 0x7FFF) val = 0x7FFF; - Capture[1]->Run(val); + Capture[1].Run(val); } // final output @@ -782,20 +870,20 @@ void Mix(u32 dummy) break; case 0x0100: // channel 1 { - s32 pan = 128 - Channels[1]->Pan; + s32 pan = 128 - Channels[1].Pan; leftoutput = ((s64)ch1 * pan) >> 10; } break; case 0x0200: // channel 3 { - s32 pan = 128 - Channels[3]->Pan; + s32 pan = 128 - Channels[3].Pan; leftoutput = ((s64)ch3 * pan) >> 10; } break; case 0x0300: // channel 1+3 { - s32 pan1 = 128 - Channels[1]->Pan; - s32 pan3 = 128 - Channels[3]->Pan; + s32 pan1 = 128 - Channels[1].Pan; + s32 pan3 = 128 - Channels[3].Pan; leftoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10); } break; @@ -808,20 +896,20 @@ void Mix(u32 dummy) break; case 0x0400: // channel 1 { - s32 pan = Channels[1]->Pan; + s32 pan = Channels[1].Pan; rightoutput = ((s64)ch1 * pan) >> 10; } break; case 0x0800: // channel 3 { - s32 pan = Channels[3]->Pan; + s32 pan = Channels[3].Pan; rightoutput = ((s64)ch3 * pan) >> 10; } break; case 0x0C00: // channel 1+3 { - s32 pan1 = Channels[1]->Pan; - s32 pan3 = Channels[3]->Pan; + s32 pan1 = Channels[1].Pan; + s32 pan3 = Channels[3].Pan; rightoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10); } break; @@ -856,14 +944,18 @@ void Mix(u32 dummy) // OutputBufferFrame can never get full because it's // transfered to OutputBuffer at the end of the frame - OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1; - OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1; - OutputBackbufferWritePosition += 2; + // FIXME: apparently this does happen!!! + if (OutputBackbufferWritePosition * 2 < OutputBufferSize - 1) + { + OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1; + OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1; + OutputBackbufferWritePosition += 2; + } - NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0); + NDS.ScheduleEvent(Event_SPU, true, 1024, 0, 0); } -void TransferOutput() +void SPU::TransferOutput() { Platform::Mutex_Lock(AudioLock); for (u32 i = 0; i < OutputBackbufferWritePosition; i += 2) @@ -881,10 +973,10 @@ void TransferOutput() } } OutputBackbufferWritePosition = 0; - Platform::Mutex_Unlock(AudioLock); + Platform::Mutex_Unlock(AudioLock);; } -void TrimOutput() +void SPU::TrimOutput() { Platform::Mutex_Lock(AudioLock); const int halflimit = (OutputBufferSize / 2); @@ -896,7 +988,7 @@ void TrimOutput() Platform::Mutex_Unlock(AudioLock); } -void DrainOutput() +void SPU::DrainOutput() { Platform::Mutex_Lock(AudioLock); OutputFrontBufferWritePosition = 0; @@ -904,7 +996,7 @@ void DrainOutput() Platform::Mutex_Unlock(AudioLock); } -void InitOutput() +void SPU::InitOutput() { Platform::Mutex_Lock(AudioLock); memset(OutputBackbuffer, 0, 2*OutputBufferSize*2); @@ -914,7 +1006,7 @@ void InitOutput() Platform::Mutex_Unlock(AudioLock); } -int GetOutputSize() +int SPU::GetOutputSize() const { Platform::Mutex_Lock(AudioLock); @@ -930,7 +1022,7 @@ int GetOutputSize() return ret; } -void Sync(bool wait) +void SPU::Sync(bool wait) { // this function is currently not used anywhere // depending on the usage context the thread safety measures could be made @@ -960,7 +1052,7 @@ void Sync(bool wait) } } -int ReadOutput(s16* data, int samples) +int SPU::ReadOutput(s16* data, int samples) { Platform::Mutex_Lock(AudioLock); if (OutputFrontBufferReadPosition == OutputFrontBufferWritePosition) @@ -989,11 +1081,11 @@ int ReadOutput(s16* data, int samples) } -u8 Read8(u32 addr) +u8 SPU::Read8(u32 addr) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1010,8 +1102,8 @@ u8 Read8(u32 addr) case 0x04000500: return Cnt & 0x7F; case 0x04000501: return Cnt >> 8; - case 0x04000508: return Capture[0]->Cnt; - case 0x04000509: return Capture[1]->Cnt; + case 0x04000508: return Capture[0].Cnt; + case 0x04000509: return Capture[1].Cnt; } } @@ -1019,11 +1111,11 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 SPU::Read16(u32 addr) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1038,7 +1130,7 @@ u16 Read16(u32 addr) case 0x04000500: return Cnt; case 0x04000504: return Bias; - case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8); + case 0x04000508: return Capture[0].Cnt | (Capture[1].Cnt << 8); } } @@ -1046,11 +1138,11 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 SPU::Read32(u32 addr) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1064,10 +1156,10 @@ u32 Read32(u32 addr) case 0x04000500: return Cnt; case 0x04000504: return Bias; - case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8); + case 0x04000508: return Capture[0].Cnt | (Capture[1].Cnt << 8); - case 0x04000510: return Capture[0]->DstAddr; - case 0x04000518: return Capture[1]->DstAddr; + case 0x04000510: return Capture[0].DstAddr; + case 0x04000518: return Capture[1].DstAddr; } } @@ -1075,11 +1167,11 @@ u32 Read32(u32 addr) return 0; } -void Write8(u32 addr, u8 val) +void SPU::Write8(u32 addr, u8 val) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1103,11 +1195,11 @@ void Write8(u32 addr, u8 val) return; case 0x04000508: - Capture[0]->SetCnt(val); + Capture[0].SetCnt(val); if (val & 0x03) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val); return; case 0x04000509: - Capture[1]->SetCnt(val); + Capture[1].SetCnt(val); if (val & 0x03) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val); return; } @@ -1116,11 +1208,11 @@ void Write8(u32 addr, u8 val) Log(LogLevel::Warn, "unknown SPU write8 %08X %02X\n", addr, val); } -void Write16(u32 addr, u16 val) +void SPU::Write16(u32 addr, u16 val) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1128,8 +1220,8 @@ void Write16(u32 addr, u16 val) case 0x2: chan->SetCnt((chan->Cnt & 0x0000FFFF) | (val << 16)); return; case 0x8: chan->SetTimerReload(val); - if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val); - else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val); + if ((addr & 0xF0) == 0x10) Capture[0].SetTimerReload(val); + else if ((addr & 0xF0) == 0x30) Capture[1].SetTimerReload(val); return; case 0xA: chan->SetLoopPos(val); return; @@ -1152,24 +1244,24 @@ void Write16(u32 addr, u16 val) return; case 0x04000508: - Capture[0]->SetCnt(val & 0xFF); - Capture[1]->SetCnt(val >> 8); + Capture[0].SetCnt(val & 0xFF); + Capture[1].SetCnt(val >> 8); if (val & 0x0303) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val); return; - case 0x04000514: Capture[0]->SetLength(val); return; - case 0x0400051C: Capture[1]->SetLength(val); return; + case 0x04000514: Capture[0].SetLength(val); return; + case 0x0400051C: Capture[1].SetLength(val); return; } } Log(LogLevel::Warn, "unknown SPU write16 %08X %04X\n", addr, val); } -void Write32(u32 addr, u32 val) +void SPU::Write32(u32 addr, u32 val) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1179,8 +1271,8 @@ void Write32(u32 addr, u32 val) chan->SetLoopPos(val >> 16); val &= 0xFFFF; chan->SetTimerReload(val); - if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val); - else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val); + if ((addr & 0xF0) == 0x10) Capture[0].SetTimerReload(val); + else if ((addr & 0xF0) == 0x30) Capture[1].SetTimerReload(val); return; case 0xC: chan->SetLength(val); return; } @@ -1200,15 +1292,15 @@ void Write32(u32 addr, u32 val) return; case 0x04000508: - Capture[0]->SetCnt(val & 0xFF); - Capture[1]->SetCnt(val >> 8); + Capture[0].SetCnt(val & 0xFF); + Capture[1].SetCnt(val >> 8); if (val & 0x0303) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val); return; - case 0x04000510: Capture[0]->SetDstAddr(val); return; - case 0x04000514: Capture[0]->SetLength(val & 0xFFFF); return; - case 0x04000518: Capture[1]->SetDstAddr(val); return; - case 0x0400051C: Capture[1]->SetLength(val & 0xFFFF); return; + case 0x04000510: Capture[0].SetDstAddr(val); return; + case 0x04000514: Capture[0].SetLength(val & 0xFFFF); return; + case 0x04000518: Capture[1].SetDstAddr(val); return; + case 0x0400051C: Capture[1].SetLength(val & 0xFFFF); return; } } } diff --git a/src/SPU.h b/src/SPU.h index 1f28c2f8..6e3d1aae 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,81 +20,74 @@ #define SPU_H #include "Savestate.h" +#include "Platform.h" -namespace SPU +namespace melonDS { +class NDS; +class SPU; -bool Init(); -void DeInit(); -void Reset(); -void Stop(); +enum class AudioBitDepth +{ + Auto, + _10Bit, + _16Bit, +}; -void DoSavestate(Savestate* file); +enum class AudioInterpolation +{ + None, + Linear, + Cosine, + Cubic, + SNESGaussian +}; -void SetPowerCnt(u32 val); - -// 0=none 1=linear 2=cosine 3=cubic -void SetInterpolation(int type); - -void SetBias(u16 bias); -void SetDegrade10Bit(bool enable); -void SetApplyBias(bool enable); - -void Mix(u32 dummy); - -void TrimOutput(); -void DrainOutput(); -void InitOutput(); -int GetOutputSize(); -void Sync(bool wait); -int ReadOutput(s16* data, int samples); -void TransferOutput(); - -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); - -class Channel +class SPUChannel { public: - Channel(u32 num); - ~Channel(); + SPUChannel(u32 num, melonDS::NDS& nds, AudioInterpolation interpolation); void Reset(); void DoSavestate(Savestate* file); - u32 Num; + static const s8 ADPCMIndexTable[8]; + static const u16 ADPCMTable[89]; + static const s16 PSGTable[8][8]; - u32 Cnt; - u32 SrcAddr; - u16 TimerReload; - u32 LoopPos; - u32 Length; + // audio interpolation is an improvement upon the original hardware + // (which performs no interpolation) + AudioInterpolation InterpType = AudioInterpolation::None; - u8 Volume; - u8 VolumeShift; - u8 Pan; + const u32 Num; - bool KeyOn; - u32 Timer; - s32 Pos; - s16 PrevSample[3]; - s16 CurSample; - u16 NoiseVal; + u32 Cnt = 0; + u32 SrcAddr = 0; + u16 TimerReload = 0; + u32 LoopPos = 0; + u32 Length = 0; - s32 ADPCMVal; - s32 ADPCMIndex; - s32 ADPCMValLoop; - s32 ADPCMIndexLoop; - u8 ADPCMCurByte; + u8 Volume = 0; + u8 VolumeShift = 0; + u8 Pan = 0; - u32 FIFO[8]; - u32 FIFOReadPos; - u32 FIFOWritePos; - u32 FIFOReadOffset; - u32 FIFOLevel; + bool KeyOn = false; + u32 Timer = 0; + s32 Pos = 0; + s16 PrevSample[3] {}; + s16 CurSample = 0; + u16 NoiseVal = 0; + + s32 ADPCMVal = 0; + s32 ADPCMIndex = 0; + s32 ADPCMValLoop = 0; + s32 ADPCMIndexLoop = 0; + u8 ADPCMCurByte = 0; + + u32 FIFO[8] {}; + u32 FIFOReadPos = 0; + u32 FIFOWritePos = 0; + u32 FIFOReadOffset = 0; + u32 FIFOLevel = 0; void FIFO_BufferData(); template T FIFO_ReadData(); @@ -161,32 +154,31 @@ public: void PanOutput(s32 in, s32& left, s32& right); private: - u32 (*BusRead32)(u32 addr); + melonDS::NDS& NDS; }; -class CaptureUnit +class SPUCaptureUnit { public: - CaptureUnit(u32 num); - ~CaptureUnit(); + SPUCaptureUnit(u32 num, melonDS::NDS&); void Reset(); void DoSavestate(Savestate* file); - u32 Num; + const u32 Num; - u8 Cnt; - u32 DstAddr; - u16 TimerReload; - u32 Length; + u8 Cnt = 0; + u32 DstAddr = 0; + u16 TimerReload = 0; + u32 Length = 0; - u32 Timer; - s32 Pos; + u32 Timer = 0; + s32 Pos = 0; - u32 FIFO[4]; - u32 FIFOReadPos; - u32 FIFOWritePos; - u32 FIFOWriteOffset; - u32 FIFOLevel; + u32 FIFO[4] {}; + u32 FIFOReadPos = 0; + u32 FIFOWritePos = 0; + u32 FIFOWriteOffset = 0; + u32 FIFOLevel = 0; void FIFO_FlushData(); template void FIFO_WriteData(T val); @@ -218,9 +210,67 @@ public: void Run(s32 sample); private: - void (*BusWrite32)(u32 addr, u32 val); + melonDS::NDS& NDS; +}; + +class SPU +{ +public: + explicit SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation); + ~SPU(); + void Reset(); + void DoSavestate(Savestate* file); + + void Stop(); + + void SetPowerCnt(u32 val); + + // 0=none 1=linear 2=cosine 3=cubic + void SetInterpolation(AudioInterpolation type); + + void SetBias(u16 bias); + void SetDegrade10Bit(bool enable); + void SetDegrade10Bit(AudioBitDepth depth); + void SetApplyBias(bool enable); + + void Mix(u32 dummy); + + void TrimOutput(); + void DrainOutput(); + void InitOutput(); + int GetOutputSize() const; + void Sync(bool wait); + int ReadOutput(s16* data, int samples); + void TransferOutput(); + + u8 Read8(u32 addr); + u16 Read16(u32 addr); + u32 Read32(u32 addr); + void Write8(u32 addr, u8 val); + void Write16(u32 addr, u16 val); + void Write32(u32 addr, u32 val); + +private: + static const u32 OutputBufferSize = 2*2048; + melonDS::NDS& NDS; + s16 OutputBackbuffer[2 * OutputBufferSize] {}; + u32 OutputBackbufferWritePosition = 0; + + s16 OutputFrontBuffer[2 * OutputBufferSize] {}; + u32 OutputFrontBufferWritePosition = 0; + u32 OutputFrontBufferReadPosition = 0; + + Platform::Mutex* AudioLock; + + u16 Cnt = 0; + u8 MasterVolume = 0; + u16 Bias = 0; + bool ApplyBias = true; + bool Degrade10Bit = false; + + std::array Channels; + std::array Capture; }; } - #endif // SPU_H diff --git a/src/Savestate.cpp b/src/Savestate.cpp index 546c16e0..6d6a9a47 100644 --- a/src/Savestate.cpp +++ b/src/Savestate.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,6 +22,8 @@ #include "Savestate.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -378,4 +380,6 @@ u32 Savestate::FindSection(const char* magic) const // We've reached the end of the file without finding the requested section... Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic); return NO_SECTION; +} + } \ No newline at end of file diff --git a/src/Savestate.h b/src/Savestate.h index 0aef517e..2e1400a0 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,9 +24,11 @@ #include #include "types.h" -#define SAVESTATE_MAJOR 10 -#define SAVESTATE_MINOR 0 +#define SAVESTATE_MAJOR 12 +#define SAVESTATE_MINOR 1 +namespace melonDS +{ class Savestate { public: @@ -117,5 +119,6 @@ private: bool buffer_owned; bool finished; }; +} #endif // SAVESTATE_H diff --git a/src/TinyVector.h b/src/TinyVector.h new file mode 100644 index 00000000..5a30ff65 --- /dev/null +++ b/src/TinyVector.h @@ -0,0 +1,137 @@ +/* + Copyright 2016-2023 melonDS team, RSDuck + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_TINYVECTOR_H +#define MELONDS_TINYVECTOR_H + +#include +#include +#include "types.h" + +namespace melonDS +{ +/* + TinyVector + - because reinventing the wheel is the best! + + - meant to be used very often, with not so many elements + max 1 << 16 elements + - doesn't allocate while no elements are inserted + - not stl confirmant of course + - probably only works with POD types + - remove operations don't preserve order, but O(1)! +*/ +template +struct __attribute__((packed)) TinyVector +{ + T* Data = NULL; + u16 Capacity = 0; + u16 Length = 0; + + ~TinyVector() + { + delete[] Data; + } + + void MakeCapacity(u32 capacity) + { + assert(capacity <= UINT16_MAX); + assert(capacity > Capacity); + T* newMem = new T[capacity]; + if (Data != NULL) + memcpy(newMem, Data, sizeof(T) * Length); + + T* oldData = Data; + Data = newMem; + if (oldData != NULL) + delete[] oldData; + + Capacity = capacity; + } + + void SetLength(u16 length) + { + if (Capacity < length) + MakeCapacity(length); + + Length = length; + } + + void Clear() + { + Length = 0; + } + + void Add(T element) + { + assert(Length + 1 <= UINT16_MAX); + if (Length + 1 > Capacity) + MakeCapacity(((Capacity + 4) * 3) / 2); + + Data[Length++] = element; + } + + void Remove(int index) + { + assert(Length > 0); + assert(index >= 0 && index < Length); + + Length--; + Data[index] = Data[Length]; + /*for (int i = index; i < Length; i++) + Data[i] = Data[i + 1];*/ + } + + int Find(T needle) const + { + for (int i = 0; i < Length; i++) + { + if (Data[i] == needle) + return i; + } + return -1; + } + + bool RemoveByValue(T needle) + { + for (int i = 0; i < Length; i++) + { + if (Data[i] == needle) + { + Remove(i); + return true; + } + } + return false; + } + + T& operator[](int index) + { + assert(index >= 0 && index < Length); + return Data[index]; + } + + const T& operator[](int index) const + { + assert(index >= 0 && index < Length); + return Data[index]; + } +}; +} + +#endif //MELONDS_TINYVECTOR_H diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 00000000..698cf9bd --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,66 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include "Utils.h" + +#include + +namespace melonDS +{ +std::pair, u32> PadToPowerOf2(std::unique_ptr&& data, u32 len) noexcept +{ + if (data == nullptr || len == 0) + return {nullptr, 0}; + + if ((len & (len - 1)) == 0) + return {std::move(data), len}; + + u32 newlen = 1; + while (newlen < len) + newlen <<= 1; + + auto newdata = std::make_unique(newlen); + memcpy(newdata.get(), data.get(), len); + data = nullptr; + return {std::move(newdata), newlen}; +} + +std::pair, u32> PadToPowerOf2(const u8* data, u32 len) noexcept +{ + if (len == 0) + return {nullptr, 0}; + + u32 newlen = 1; + while (newlen < len) + newlen <<= 1; + + auto newdata = std::make_unique(newlen); + memcpy(newdata.get(), data, len); + return {std::move(newdata), newlen}; +} + +std::unique_ptr CopyToUnique(const u8* data, u32 len) noexcept +{ + if (data == nullptr || len == 0) + return nullptr; + + auto newdata = std::make_unique(len); + memcpy(newdata.get(), data, len); + return newdata; +} +} \ No newline at end of file diff --git a/src/Utils.h b/src/Utils.h new file mode 100644 index 00000000..63be217b --- /dev/null +++ b/src/Utils.h @@ -0,0 +1,43 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef MELONDS_UTILS_H +#define MELONDS_UTILS_H + +#include +#include "types.h" +#include + +namespace melonDS +{ +/// Ensures that the given array is a power of 2 in length. +/// +/// @return If \c len is a power of 2, returns \c data and \c len unchanged +/// without copying anything. +/// If \c data is \c nullptr, returns {nullptr, 0}. +/// Otherwise, return a copy of \c data with zero-padding to the next power of 2. +/// @post \c data is \c nullptr, even if it doesn't need to be copied. +std::pair, u32> PadToPowerOf2(std::unique_ptr&& data, u32 len) noexcept; + +std::pair, u32> PadToPowerOf2(const u8* data, u32 len) noexcept; + +std::unique_ptr CopyToUnique(const u8* data, u32 len) noexcept; + +} + +#endif // MELONDS_UTILS_H diff --git a/src/Wifi.cpp b/src/Wifi.cpp index d786cd05..d8f440b4 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,94 +24,24 @@ #include "WifiAP.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace Wifi -{ //#define WIFI_LOG printf #define WIFI_LOG(...) {} #define PRINT_MAC(pf, mac) Log(LogLevel::Debug, "%s: %02X:%02X:%02X:%02X:%02X:%02X\n", pf, (mac)[0], (mac)[1], (mac)[2], (mac)[3], (mac)[4], (mac)[5]); -u8 RAM[0x2000]; -u16 IO[0x1000>>1]; - #define IOPORT(x) IO[(x)>>1] #define IOPORT8(x) ((u8*)IO)[x] // destination MACs for MP frames -const u8 MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00}; -const u8 MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10}; -const u8 MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03}; - -const int kTimerInterval = 8; -const u32 kTimeCheckMask = ~(kTimerInterval - 1); - -bool Enabled; -bool PowerOn; - -s32 TimerError; - -u16 Random; - -// general, always-on microsecond counter -u64 USTimestamp; - -u64 USCounter; -u64 USCompare; -bool BlockBeaconIRQ14; - -u32 CmdCounter; - -u8 BBRegs[0x100]; -u8 BBRegsRO[0x100]; - -u8 RFVersion; -u32 RFRegs[0x40]; - -struct TXSlot -{ - bool Valid; - u16 Addr; - u16 Length; - u8 Rate; - u8 CurPhase; - int CurPhaseTime; - u32 HalfwordTimeMask; -}; - -TXSlot TXSlots[6]; -u8 TXBuffer[0x2000]; - -u8 RXBuffer[2048]; -u32 RXBufferPtr; -int RXTime; -u32 RXHalfwordTimeMask; - -u32 ComStatus; // 0=waiting for packets 1=receiving 2=sending -u32 TXCurSlot; -u32 RXCounter; - -int MPReplyTimer; -u16 MPClientMask, MPClientFail; - -u8 MPClientReplies[15*1024]; - -u16 MPLastSeqno; - -bool MPInited; -bool LANInited; - -int USUntilPowerOn; -bool ForcePowerOn; - -// MULTIPLAYER SYNC APPARATUS -bool IsMP; -bool IsMPClient; -u64 NextSync; // for clients: timestamp for next sync point -u64 RXTimestamp; +const u8 Wifi::MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00}; +const u8 Wifi::MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10}; +const u8 Wifi::MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03}; // multiplayer host TX sequence: // 1. preamble @@ -148,8 +78,21 @@ u64 RXTimestamp; // * TX errors (if applicable) -bool Init() +bool MACEqual(const u8* a, const u8* b) { + return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]); +} + +bool MACIsBroadcast(const u8* a) +{ + return (*(u32*)&a[0] == 0xFFFFFFFF) && (*(u16*)&a[4] == 0xFFFF); +} + + +Wifi::Wifi(melonDS::NDS& nds) : NDS(nds) +{ + NDS.RegisterEventFunc(Event_Wifi, 0, MemberEventFunc(Wifi, USTimer)); + //MPInited = false; //LANInited = false; @@ -159,24 +102,23 @@ bool Init() Platform::LAN_Init(); LANInited = true; - WifiAP::Init(); - - return true; + WifiAP = new class WifiAP(this); } -void DeInit() +Wifi::~Wifi() { if (MPInited) Platform::MP_DeInit(); if (LANInited) Platform::LAN_DeInit(); - WifiAP::DeInit(); + delete WifiAP; WifiAP = nullptr; + + NDS.UnregisterEventFunc(Event_Wifi, 0); } -void Reset() +void Wifi::Reset() { - using namespace SPI_Firmware; memset(RAM, 0, 0x2000); memset(IO, 0, 0x1000); @@ -216,15 +158,51 @@ void Reset() } #undef BBREG_FIXED - RFVersion = GetFirmware()->Header().RFChipType; + const Firmware& fw = NDS.SPI.GetFirmware(); + const auto& fwheader = fw.GetHeader(); + + RFVersion = fwheader.RFChipType; memset(RFRegs, 0, 4*0x40); - FirmwareConsoleType console = GetFirmware()->Header().ConsoleType; - if (console == FirmwareConsoleType::DS) + // load channel index/data from the firmware + // the current channel will be determined by RF settings + // so we compare the two 'most important' RF registers to these values to figure out which channel is selected + + if (RFVersion == 3) + { + RFChannelIndex[0] = fwheader.Type3Config.RFIndex1; + RFChannelIndex[1] = fwheader.Type3Config.RFIndex2; + + for (int i = 0; i < 14; i++) + { + RFChannelData[i][0] = fwheader.Type3Config.RFData1[i]; + RFChannelData[i][1] = fwheader.Type3Config.RFData2[i]; + } + } + else + { + RFChannelIndex[0] = fwheader.Type2Config.InitialRF56Values[2] >> 2; + RFChannelIndex[1] = fwheader.Type2Config.InitialRF56Values[5] >> 2; + + for (int i = 0; i < 14; i++) + { + RFChannelData[i][0] = fwheader.Type2Config.InitialRF56Values[i*6 + 0] | + (fwheader.Type2Config.InitialRF56Values[i*6 + 1] << 8) | + ((fwheader.Type2Config.InitialRF56Values[i*6 + 2] & 0x03) << 16); + RFChannelData[i][1] = fwheader.Type2Config.InitialRF56Values[i*6 + 3] | + (fwheader.Type2Config.InitialRF56Values[i*6 + 4] << 8) | + ((fwheader.Type2Config.InitialRF56Values[i*6 + 5] & 0x03) << 16); + } + } + + CurChannel = 0; + + Firmware::FirmwareConsoleType console = fwheader.ConsoleType; + if (console == Firmware::FirmwareConsoleType::DS) IOPORT(0x000) = 0x1440; - else if (console == FirmwareConsoleType::DSLite) + else if (console == Firmware::FirmwareConsoleType::DSLite) IOPORT(0x000) = 0xC340; - else if (NDS::ConsoleType == 1 && console == FirmwareConsoleType::DSi) + else if (NDS.ConsoleType == 1 && console == Firmware::FirmwareConsoleType::DSi) IOPORT(0x000) = 0xC340; // DSi has the modern DS-wifi variant else { @@ -238,6 +216,8 @@ void Reset() // TODO: find out what the initial values are IOPORT(W_PowerUS) = 0x0001; + //IOPORT(W_BeaconInterval) = 100; + USTimestamp = 0; USCounter = 0; @@ -265,17 +245,16 @@ void Reset() CmdCounter = 0; USUntilPowerOn = 0; - ForcePowerOn = false; IsMP = false; IsMPClient = false; NextSync = 0; RXTimestamp = 0; - WifiAP::Reset(); + WifiAP->Reset(); } -void DoSavestate(Savestate* file) +void Wifi::DoSavestate(Savestate* file) { file->Section("WIFI"); @@ -300,6 +279,8 @@ void DoSavestate(Savestate* file) file->Var8(&RFVersion); file->VarArray(RFRegs, 4*0x40); + file->Var32((u32*)&CurChannel); + file->Var64(&USCounter); file->Var64(&USCompare); file->Bool32(&BlockBeaconIRQ14); @@ -341,7 +322,6 @@ void DoSavestate(Savestate* file) file->Var16(&MPLastSeqno); file->Var32((u32*)&USUntilPowerOn); - file->Bool32(&ForcePowerOn); file->Bool32(&IsMP); file->Bool32(&IsMPClient); @@ -350,7 +330,7 @@ void DoSavestate(Savestate* file) } -void ScheduleTimer(bool first) +void Wifi::ScheduleTimer(bool first) { if (first) TimerError = 0; @@ -359,14 +339,14 @@ void ScheduleTimer(bool first) s32 delay = (cycles + 999999) / 1000000; TimerError = (delay * 1000000) - cycles; - NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, USTimer, 0); + NDS.ScheduleEvent(Event_Wifi, !first, delay, 0, 0); } -void UpdatePowerOn() +void Wifi::UpdatePowerOn() { bool on = Enabled; - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { // TODO for DSi: // * W_POWER_US doesn't work (atleast on DWM-W024) @@ -394,49 +374,51 @@ void UpdatePowerOn() { Log(LogLevel::Debug, "WIFI: OFF\n"); - NDS::CancelEvent(NDS::Event_Wifi); + NDS.CancelEvent(Event_Wifi); Platform::MP_End(); } } -void SetPowerCnt(u32 val) +void Wifi::SetPowerCnt(u32 val) { Enabled = val & (1<<1); UpdatePowerOn(); } -void PowerDown(); -void StartTX_Beacon(); - -void SetIRQ(u32 irq) +void Wifi::CheckIRQ(u16 oldflags) { - u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE); - - IOPORT(W_IF) |= (1<Addr + 0x4]; if (cnt < 0xFF) cnt++; *(u16*)&RAM[slot->Addr + 0x4] = cnt; } -void ReportMPReplyErrors(u16 clientfail) +void Wifi::ReportMPReplyErrors(u16 clientfail) { // TODO: do these trigger any IRQ? @@ -542,7 +612,7 @@ void ReportMPReplyErrors(u16 clientfail) } } -void TXSendFrame(TXSlot* slot, int num) +void Wifi::TXSendFrame(const TXSlot* slot, int num) { u32 noseqno = 0; @@ -586,13 +656,16 @@ void TXSendFrame(TXSlot* slot, int num) if (noseqno == 2) *(u16*)&TXBuffer[0xC] |= (1<<11); + if (CurChannel == 0) return; + TXBuffer[9] = CurChannel; + switch (num) { case 0: case 2: case 3: Platform::MP_SendPacket(TXBuffer, 12+len, USTimestamp); - if (!IsMP) WifiAP::SendPacket(TXBuffer, 12+len); + if (!IsMP) WifiAP->SendPacket(TXBuffer, 12+len); break; case 1: @@ -612,7 +685,7 @@ void TXSendFrame(TXSlot* slot, int num) } } -void StartTX_LocN(int nslot, int loc) +void Wifi::StartTX_LocN(int nslot, int loc) { TXSlot* slot = &TXSlots[nslot]; @@ -632,7 +705,7 @@ void StartTX_LocN(int nslot, int loc) slot->CurPhaseTime = PreambleLen(slot->Rate); } -void StartTX_Cmd() +void Wifi::StartTX_Cmd() { TXSlot* slot = &TXSlots[1]; @@ -665,9 +738,12 @@ void StartTX_Cmd() slot->CurPhase = 13; slot->CurPhaseTime = CmdCounter - 100; } + + // starting a CMD transfer wakes up the transceiver automatically + UpdatePowerStatus(1); } -void StartTX_Beacon() +void Wifi::StartTX_Beacon() { TXSlot* slot = &TXSlots[4]; @@ -686,7 +762,7 @@ void StartTX_Beacon() IOPORT(W_TXBusy) |= 0x0010; } -void FireTX() +void Wifi::FireTX() { if (!(IOPORT(W_RXCnt) & 0x8000)) return; @@ -731,7 +807,7 @@ void FireTX() } } -void SendMPDefaultReply() +void Wifi::SendMPDefaultReply() { u8 reply[12 + 28]; @@ -743,6 +819,9 @@ void SendMPDefaultReply() // TODO reply[0x8] = 0x14; + if (CurChannel == 0) return; + reply[0x9] = CurChannel; + *(u16*)&reply[0xC + 0x00] = 0x0158; *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO?? *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0); @@ -761,7 +840,7 @@ void SendMPDefaultReply() WIFI_LOG("wifi: sent %d/40 bytes of MP default reply\n", txlen); } -void SendMPReply(u16 clienttime, u16 clientmask) +void Wifi::SendMPReply(u16 clienttime, u16 clientmask) { TXSlot* slot = &TXSlots[5]; @@ -822,7 +901,7 @@ void SendMPReply(u16 clienttime, u16 clientmask) IOPORT(W_TXBusy) |= 0x0080; } -void SendMPAck(u16 cmdcount, u16 clientfail) +void Wifi::SendMPAck(u16 cmdcount, u16 clientfail) { u8 ack[12 + 32]; @@ -832,6 +911,9 @@ void SendMPAck(u16 cmdcount, u16 clientfail) if (TXSlots[1].Rate == 2) ack[0x8] = 0x14; else ack[0x8] = 0xA; + if (CurChannel == 0) return; + ack[0x9] = CurChannel; + *(u16*)&ack[0xC + 0x00] = 0x0218; *(u16*)&ack[0xC + 0x02] = 0; *(u16*)&ack[0xC + 0x04] = 0x0903; @@ -868,10 +950,7 @@ void SendMPAck(u16 cmdcount, u16 clientfail) WIFI_LOG("wifi: sent %d/44 bytes of MP ack, %d %d\n", txlen, ComStatus, RXTime); } -bool CheckRX(int type); -void MPClientReplyRX(int client); - -bool ProcessTX(TXSlot* slot, int num) +bool Wifi::ProcessTX(TXSlot* slot, int num) { slot->CurPhaseTime -= kTimerInterval; if (slot->CurPhaseTime > 0) @@ -1132,7 +1211,7 @@ bool ProcessTX(TXSlot* slot, int num) } -inline void IncrementRXAddr(u16& addr, u16 inc = 2) +inline void Wifi::IncrementRXAddr(u16& addr, u16 inc) { for (u32 i = 0; i < inc; i += 2) { @@ -1143,7 +1222,7 @@ inline void IncrementRXAddr(u16& addr, u16 inc = 2) } } -void StartRX() +void Wifi::StartRX() { u16 framelen = *(u16*)&RXBuffer[8]; RXTime = framelen; @@ -1171,15 +1250,18 @@ void StartRX() ComStatus |= 1; } -void FinishRX() +void Wifi::FinishRX() { ComStatus &= ~0x1; RXCounter = 0; if (!ComStatus) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) + { + IOPORT(W_TRXPower) = 0; SetStatus(9); + } else SetStatus(1); } @@ -1446,9 +1528,9 @@ void FinishRX() } } -void MPClientReplyRX(int client) +void Wifi::MPClientReplyRX(int client) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) return; if (!(IOPORT(W_RXCnt) & 0x8000)) @@ -1487,9 +1569,9 @@ void MPClientReplyRX(int client) StartRX(); } -bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames +bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) return false; if (!(IOPORT(W_RXCnt) & 0x8000)) @@ -1501,7 +1583,7 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames int rxlen; int framelen; u16 framectl; - u8 txrate; + u8 txrate, chan; u64 timestamp; for (;;) @@ -1512,7 +1594,7 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames { rxlen = Platform::MP_RecvPacket(RXBuffer, ×tamp); if ((rxlen <= 0) && (!IsMP)) - rxlen = WifiAP::RecvPacket(RXBuffer); + rxlen = WifiAP->RecvPacket(RXBuffer); } else { @@ -1536,6 +1618,24 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames continue; } + chan = RXBuffer[9]; + if (chan != CurChannel || CurChannel == 0) + { + Log(LogLevel::Debug, "received frame but bad channel %d (expected %d)\n", chan, CurChannel); + continue; + } + + // hack: ignore MP frames if not engaged in a MP comm + if (type == 0 && (!IsMP)) + { + if (MACEqual(&RXBuffer[12 + 16], MPReplyMAC) || + MACEqual(&RXBuffer[12 + 4], MPCmdMAC) || + MACEqual(&RXBuffer[12 + 4], MPReplyMAC)) + { + continue; + } + } + framectl = *(u16*)&RXBuffer[12+0]; txrate = RXBuffer[8]; @@ -1596,7 +1696,6 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames // we also need to determine how far we can run after having received this frame RXTimestamp = timestamp; - //if (RXTimestamp < USTimestamp) printf("CRAP!! %04X %016llX %016llX\n", framectl, RXTimestamp, USTimestamp); if (RXTimestamp < USTimestamp) RXTimestamp = USTimestamp; NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8)); @@ -1627,7 +1726,7 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames } -void MSTimer() +void Wifi::MSTimer() { if (IOPORT(W_USCompareCnt)) { @@ -1638,11 +1737,13 @@ void MSTimer() } } - IOPORT(W_BeaconCount1)--; - if (IOPORT(W_BeaconCount1) == 0) + if (IOPORT(W_BeaconCount1) != 0) { - SetIRQ14(1); + IOPORT(W_BeaconCount1)--; + if (IOPORT(W_BeaconCount1) == 0) SetIRQ14(1); } + if (IOPORT(W_BeaconCount1) == 0) + IOPORT(W_BeaconCount1) = IOPORT(W_BeaconInterval); if (IOPORT(W_BeaconCount2) != 0) { @@ -1651,7 +1752,7 @@ void MSTimer() } } -void USTimer(u32 param) +void Wifi::USTimer(u32 param) { USTimestamp += kTimerInterval; @@ -1671,21 +1772,21 @@ void USTimer(u32 param) } if (!(USTimestamp & 0x3FF & kTimeCheckMask)) - WifiAP::MSTimer(); + WifiAP->MSTimer(); - bool switchOffPowerSaving = false; if (USUntilPowerOn < 0) { USUntilPowerOn += kTimerInterval; - switchOffPowerSaving = (USUntilPowerOn >= 0) && (IOPORT(W_PowerUnk) & 0x0001 || ForcePowerOn); - } - if ((USUntilPowerOn >= 0) && (IOPORT(W_PowerState) & 0x0002 || switchOffPowerSaving)) - { - IOPORT(W_PowerState) = 0; - IOPORT(W_RFPins) = 1; - IOPORT(W_RFPins) = 0x0084; - SetIRQ(11); + if (USUntilPowerOn >= 0) + { + USUntilPowerOn = 0; + + IOPORT(W_PowerState) = 0; + SetStatus(1); + + UpdatePowerStatus(0); + } } if (IOPORT(W_USCountCnt)) @@ -1728,7 +1829,7 @@ void USTimer(u32 param) u16 txbusy = IOPORT(W_TXBusy); if (txbusy) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) { ComStatus = 0; TXCurSlot = -1; @@ -1763,9 +1864,10 @@ void USTimer(u32 param) bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot); if (finished) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) { IOPORT(W_TXBusy) = 0; + IOPORT(W_TRXPower) = 0; SetStatus(9); } @@ -1822,8 +1924,9 @@ void USTimer(u32 param) RXCounter = 0; } // TODO: proper error management - if ((!ComStatus) && (IOPORT(W_PowerState) & 0x0300)) + if ((!ComStatus) && (IOPORT(W_PowerState) & (1<<9))) { + IOPORT(W_TRXPower) = 0; SetStatus(9); } } @@ -1834,7 +1937,29 @@ void USTimer(u32 param) } -void RFTransfer_Type2() +void Wifi::ChangeChannel() +{ + u32 val1 = RFRegs[RFChannelIndex[0]]; + u32 val2 = RFRegs[RFChannelIndex[1]]; + + CurChannel = 0; + + for (int i = 0; i < 14; i++) + { + if (val1 == RFChannelData[i][0] && val2 == RFChannelData[i][1]) + { + CurChannel = i+1; + break; + } + } + + if (CurChannel > 0) + Log(LogLevel::Debug, "wifi: switching to channel %d\n", CurChannel); + else + Log(LogLevel::Debug, "wifi: invalid channel values %05X:%05X\n", val1, val2); +} + +void Wifi::RFTransfer_Type2() { u32 id = (IOPORT(W_RFData2) >> 2) & 0x1F; @@ -1848,10 +1973,13 @@ void RFTransfer_Type2() { u32 data = IOPORT(W_RFData1) | ((IOPORT(W_RFData2) & 0x0003) << 16); RFRegs[id] = data; + + if (id == RFChannelIndex[0] || id == RFChannelIndex[1]) + ChangeChannel(); } } -void RFTransfer_Type3() +void Wifi::RFTransfer_Type3() { u32 id = (IOPORT(W_RFData1) >> 8) & 0x3F; @@ -1864,11 +1992,14 @@ void RFTransfer_Type3() { u32 data = IOPORT(W_RFData1) & 0xFF; RFRegs[id] = data; + + if (id == RFChannelIndex[0] || id == RFChannelIndex[1]) + ChangeChannel(); } } -u16 Read(u32 addr) +u16 Wifi::Read(u32 addr) { if (addr >= 0x04810000) return 0; @@ -1887,6 +2018,7 @@ u16 Read(u32 addr) switch (addr) { case W_Random: // random generator. not accurate + // TODO: rotate the sequence based on the ARM7 cycle counter (if this is important) Random = (Random & 0x1) ^ (((Random & 0x3FF) << 1) | (Random >> 10)); return Random; @@ -1967,11 +2099,10 @@ u16 Read(u32 addr) } } - //printf("WIFI: read %08X\n", addr); return IOPORT(addr&0xFFF); } -void Write(u32 addr, u16 val) +void Wifi::Write(u32 addr, u16 val) { if (addr >= 0x04810000) return; @@ -1991,28 +2122,20 @@ void Write(u32 addr, u16 val) case W_ModeReset: { u16 oldval = IOPORT(W_ModeReset); + IOPORT(W_ModeReset) = val & 0x0001; if (!(oldval & 0x0001) && (val & 0x0001)) { - if (!(USUntilPowerOn < 0 && ForcePowerOn)) - { - //printf("mode reset power on %08x\n", NDS::ARM7->R[15]); - IOPORT(0x034) = 0x0002; - IOPORT(0x27C) = 0x0005; - // TODO: 02A2?? + IOPORT(0x27C) = 0x0005; + // TODO: 02A2?? - if (IOPORT(W_PowerUnk) & 0x0002) - { - USUntilPowerOn = -2048; - IOPORT(W_PowerState) |= 0x100; - } - } + UpdatePowerStatus(0); } else if ((oldval & 0x0001) && !(val & 0x0001)) { - //printf("mode reset shutdown %08x\n", NDS::ARM7->R[15]); IOPORT(0x27C) = 0x000A; - PowerDown(); + + UpdatePowerStatus(0); } if (val & 0x2000) @@ -2054,23 +2177,43 @@ void Write(u32 addr, u16 val) IOPORT(0x230) = 0x0047; } } - break; + return; case W_ModeWEP: val &= 0x007F; - //printf("writing mode web %x\n", val); - if ((val & 0x7) == 1) - IOPORT(W_PowerUnk) |= 0x0002; - if ((val & 0x7) == 2) - IOPORT(W_PowerUnk) = 0x0003; - break; + IOPORT(W_ModeWEP) = val; + if (IOPORT(W_PowerTX) & (1<<1)) + { + if ((val & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((val & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + + if ((val & 0x7) != 3) + IOPORT(W_PowerState) &= 0x0300; + + UpdatePowerStatus(0); + } + return; + + case W_IE: + { + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + IOPORT(W_IE) = val; + CheckIRQ(oldflags); + } + return; case W_IF: IOPORT(W_IF) &= ~val; return; case W_IFSet: - IOPORT(W_IF) |= (val & 0xFBFF); - Log(LogLevel::Debug, "wifi: force-setting IF %04X\n", val); + { + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + IOPORT(W_IF) |= (val & 0xFBFF); + CheckIRQ(oldflags); + Log(LogLevel::Debug, "wifi: force-setting IF %04X\n", val); + } return; case W_AIDLow: @@ -2080,67 +2223,63 @@ void Write(u32 addr, u16 val) IOPORT(W_AIDFull) = val & 0x07FF; return; - case W_PowerState: - //printf("writing power state %x %08x\n", val, NDS::ARM7->R[15]); - IOPORT(W_PowerState) |= val & 0x0002; - - if (IOPORT(W_ModeReset) & 0x0001 && IOPORT(W_PowerState) & 0x0002) - { - /*if (IOPORT(W_PowerState) & 0x100) - { - AlwaysPowerOn = true; - USUntilPowerOn = -1; - } - else */ - if (IOPORT(W_PowerForce) == 1) - { - //printf("power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = false; - } - } - return; - case W_PowerForce: - //if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val); - - val &= 0x8001; - //printf("writing power force %x %08x\n", val, NDS::ARM7->R[15]); - if (val == 0x8001) - { - //printf("force power off\n"); - IOPORT(0x034) = 0x0002; - IOPORT(W_PowerState) = 0x0200; - IOPORT(W_TXReqRead) = 0; - PowerDown(); - } - if (val == 1 && IOPORT(W_PowerState) & 0x0002) - { - //printf("power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = false; - } - if (val == 0x8000) - { - //printf("force power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = true; - } - break; case W_PowerUS: IOPORT(W_PowerUS) = val & 0x0003; UpdatePowerOn(); return; - case W_PowerUnk: - val &= 0x0003; - //printf("writing power unk %x\n", val); - if ((IOPORT(W_ModeWEP) & 0x7) == 1) - val |= 2; - else if ((IOPORT(W_ModeWEP) & 0x7) == 2) - val = 3; - break; + + case W_PowerTX: + IOPORT(W_PowerTX) = val & 0x0003; + if (val & (1<<1)) + { + if ((IOPORT(W_ModeWEP) & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((IOPORT(W_ModeWEP) & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + + UpdatePowerStatus(0); + } + return; + + case W_PowerState: + if ((IOPORT(W_ModeWEP) & 0x7) != 3) + return; + + val = (IOPORT(W_PowerState) & 0x0300) | (val & 0x0003); + if ((val & 0x0300) == 0x0200) + val &= ~(1<<0); + else + val &= ~(1<<1); + + if (!(val & (1<<9))) + val &= ~(1<<8); + + IOPORT(W_PowerState) = val; + UpdatePowerStatus(0); + return; + + case W_PowerForce: + val &= 0x8001; + IOPORT(W_PowerForce) = val; + UpdatePowerStatus(0); + return; + + case W_PowerDownCtrl: + IOPORT(W_PowerDownCtrl) = val & 0x0003; + + if (IOPORT(W_PowerTX) & (1<<1)) + { + if ((IOPORT(W_ModeWEP) & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((IOPORT(W_ModeWEP) & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + } + + if (val != 0 && val != 3) + Log(LogLevel::Warn, "wifi: unusual W_PowerDownCtrl value %04X\n", val); + + UpdatePowerStatus(0); + return; case W_USCountCnt: val &= 0x0001; break; case W_USCompareCnt: @@ -2299,6 +2438,7 @@ void Write(u32 addr, u16 val) // read-only ports case 0x000: + case 0x034: case 0x044: case 0x054: case 0x098: @@ -2326,14 +2466,14 @@ void Write(u32 addr, u16 val) } -u8* GetMAC() +const u8* Wifi::GetMAC() const { return (u8*)&IOPORT(W_MACAddr0); } -u8* GetBSSID() +const u8* Wifi::GetBSSID() const { return (u8*)&IOPORT(W_BSSID0); } -} +} \ No newline at end of file diff --git a/src/Wifi.h b/src/Wifi.h index b9594f45..2e0465a6 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,178 +21,280 @@ #include "Savestate.h" -namespace Wifi +namespace melonDS { - -enum +class WifiAP; +class NDS; +class Wifi { - W_ID = 0x000, +public: - W_ModeReset = 0x004, - W_ModeWEP = 0x006, - W_TXStatCnt = 0x008, - W_IF = 0x010, - W_IE = 0x012, + enum + { + W_ID = 0x000, - W_MACAddr0 = 0x018, - W_MACAddr1 = 0x01A, - W_MACAddr2 = 0x01C, - W_BSSID0 = 0x020, - W_BSSID1 = 0x022, - W_BSSID2 = 0x024, - W_AIDLow = 0x028, - W_AIDFull = 0x02A, + W_ModeReset = 0x004, + W_ModeWEP = 0x006, + W_TXStatCnt = 0x008, + W_IF = 0x010, + W_IE = 0x012, - W_TXRetryLimit = 0x02C, - W_RXCnt = 0x030, - W_WEPCnt = 0x032, + W_MACAddr0 = 0x018, + W_MACAddr1 = 0x01A, + W_MACAddr2 = 0x01C, + W_BSSID0 = 0x020, + W_BSSID1 = 0x022, + W_BSSID2 = 0x024, + W_AIDLow = 0x028, + W_AIDFull = 0x02A, - W_PowerUS = 0x036, - W_PowerTX = 0x038, - W_PowerState = 0x03C, - W_PowerForce = 0x040, - W_PowerUnk = 0x48, + W_TXRetryLimit = 0x02C, + W_RXCnt = 0x030, + W_WEPCnt = 0x032, - W_Random = 0x044, + W_TRXPower = 0x034, + W_PowerUS = 0x036, + W_PowerTX = 0x038, + W_PowerState = 0x03C, + W_PowerForce = 0x040, + W_PowerDownCtrl = 0x48, - W_RXBufBegin = 0x050, - W_RXBufEnd = 0x052, - W_RXBufWriteCursor = 0x054, - W_RXBufWriteAddr = 0x056, - W_RXBufReadAddr = 0x058, - W_RXBufReadCursor = 0x05A, - W_RXBufCount = 0x05C, - W_RXBufDataRead = 0x060, - W_RXBufGapAddr = 0x062, - W_RXBufGapSize = 0x064, + W_Random = 0x044, - W_TXBufWriteAddr = 0x068, - W_TXBufCount = 0x06C, - W_TXBufDataWrite = 0x070, - W_TXBufGapAddr = 0x074, - W_TXBufGapSize = 0x076, + W_RXBufBegin = 0x050, + W_RXBufEnd = 0x052, + W_RXBufWriteCursor = 0x054, + W_RXBufWriteAddr = 0x056, + W_RXBufReadAddr = 0x058, + W_RXBufReadCursor = 0x05A, + W_RXBufCount = 0x05C, + W_RXBufDataRead = 0x060, + W_RXBufGapAddr = 0x062, + W_RXBufGapSize = 0x064, - W_TXSlotBeacon = 0x080, - W_TXBeaconTIM = 0x084, - W_ListenCount = 0x088, - W_BeaconInterval = 0x08C, - W_ListenInterval = 0x08E, - W_TXSlotCmd = 0x090, - W_TXSlotReply1 = 0x094, - W_TXSlotReply2 = 0x098, - W_TXSlotLoc1 = 0x0A0, - W_TXSlotLoc2 = 0x0A4, - W_TXSlotLoc3 = 0x0A8, - W_TXReqReset = 0x0AC, - W_TXReqSet = 0x0AE, - W_TXReqRead = 0x0B0, - W_TXSlotReset = 0x0B4, - W_TXBusy = 0x0B6, - W_TXStat = 0x0B8, - W_Preamble = 0x0BC, - W_CmdTotalTime = 0x0C0, - W_CmdReplyTime = 0x0C4, - W_RXFilter = 0x0D0, - W_RXLenCrop = 0x0DA, - W_RXFilter2 = 0x0E0, + W_TXBufWriteAddr = 0x068, + W_TXBufCount = 0x06C, + W_TXBufDataWrite = 0x070, + W_TXBufGapAddr = 0x074, + W_TXBufGapSize = 0x076, - W_USCountCnt = 0x0E8, - W_USCompareCnt = 0x0EA, - W_CmdCountCnt = 0x0EE, + W_TXSlotBeacon = 0x080, + W_TXBeaconTIM = 0x084, + W_ListenCount = 0x088, + W_BeaconInterval = 0x08C, + W_ListenInterval = 0x08E, + W_TXSlotCmd = 0x090, + W_TXSlotReply1 = 0x094, + W_TXSlotReply2 = 0x098, + W_TXSlotLoc1 = 0x0A0, + W_TXSlotLoc2 = 0x0A4, + W_TXSlotLoc3 = 0x0A8, + W_TXReqReset = 0x0AC, + W_TXReqSet = 0x0AE, + W_TXReqRead = 0x0B0, + W_TXSlotReset = 0x0B4, + W_TXBusy = 0x0B6, + W_TXStat = 0x0B8, + W_Preamble = 0x0BC, + W_CmdTotalTime = 0x0C0, + W_CmdReplyTime = 0x0C4, + W_RXFilter = 0x0D0, + W_RXLenCrop = 0x0DA, + W_RXFilter2 = 0x0E0, - W_USCount0 = 0x0F8, - W_USCount1 = 0x0FA, - W_USCount2 = 0x0FC, - W_USCount3 = 0x0FE, - W_USCompare0 = 0x0F0, - W_USCompare1 = 0x0F2, - W_USCompare2 = 0x0F4, - W_USCompare3 = 0x0F6, + W_USCountCnt = 0x0E8, + W_USCompareCnt = 0x0EA, + W_CmdCountCnt = 0x0EE, - W_ContentFree = 0x10C, - W_PreBeacon = 0x110, - W_CmdCount = 0x118, - W_BeaconCount1 = 0x11C, - W_BeaconCount2 = 0x134, + W_USCount0 = 0x0F8, + W_USCount1 = 0x0FA, + W_USCount2 = 0x0FC, + W_USCount3 = 0x0FE, + W_USCompare0 = 0x0F0, + W_USCompare1 = 0x0F2, + W_USCompare2 = 0x0F4, + W_USCompare3 = 0x0F6, - W_BBCnt = 0x158, - W_BBWrite = 0x15A, - W_BBRead = 0x15C, - W_BBBusy = 0x15E, - W_BBMode = 0x160, - W_BBPower = 0x168, + W_ContentFree = 0x10C, + W_PreBeacon = 0x110, + W_CmdCount = 0x118, + W_BeaconCount1 = 0x11C, + W_BeaconCount2 = 0x134, - W_RFData2 = 0x17C, - W_RFData1 = 0x17E, - W_RFBusy = 0x180, - W_RFCnt = 0x184, + W_BBCnt = 0x158, + W_BBWrite = 0x15A, + W_BBRead = 0x15C, + W_BBBusy = 0x15E, + W_BBMode = 0x160, + W_BBPower = 0x168, - W_TXHeaderCnt = 0x194, - W_RFPins = 0x19C, + W_RFData2 = 0x17C, + W_RFData1 = 0x17E, + W_RFBusy = 0x180, + W_RFCnt = 0x184, - W_RXStatIncIF = 0x1A8, - W_RXStatIncIE = 0x1AA, - W_RXStatHalfIF = 0x1AC, - W_RXStatHalfIE = 0x1AE, - W_TXErrorCount = 0x1C0, - W_RXCount = 0x1C4, + W_TXHeaderCnt = 0x194, + W_RFPins = 0x19C, - W_CMDStat0 = 0x1D0, - W_CMDStat1 = 0x1D2, - W_CMDStat2 = 0x1D4, - W_CMDStat3 = 0x1D6, - W_CMDStat4 = 0x1D8, - W_CMDStat5 = 0x1DA, - W_CMDStat6 = 0x1DC, - W_CMDStat7 = 0x1DE, + W_RXStatIncIF = 0x1A8, + W_RXStatIncIE = 0x1AA, + W_RXStatHalfIF = 0x1AC, + W_RXStatHalfIE = 0x1AE, + W_TXErrorCount = 0x1C0, + W_RXCount = 0x1C4, - W_TXSeqNo = 0x210, - W_RFStatus = 0x214, - W_IFSet = 0x21C, - W_RXTXAddr = 0x268, + W_CMDStat0 = 0x1D0, + W_CMDStat1 = 0x1D2, + W_CMDStat2 = 0x1D4, + W_CMDStat3 = 0x1D6, + W_CMDStat4 = 0x1D8, + W_CMDStat5 = 0x1DA, + W_CMDStat6 = 0x1DC, + W_CMDStat7 = 0x1DE, + + W_TXSeqNo = 0x210, + W_RFStatus = 0x214, + W_IFSet = 0x21C, + W_RXTXAddr = 0x268, + }; + + Wifi(melonDS::NDS& nds); + ~Wifi(); + void Reset(); + void DoSavestate(Savestate* file); + + void SetPowerCnt(u32 val); + + void USTimer(u32 param); + + u16 Read(u32 addr); + void Write(u32 addr, u16 val); + + const u8* GetMAC() const; + const u8* GetBSSID() const; + +private: + melonDS::NDS& NDS; + u8 RAM[0x2000]; + u16 IO[0x1000>>1]; + + static const u8 MPCmdMAC[6]; + static const u8 MPReplyMAC[6]; + static const u8 MPAckMAC[6]; + + static const int kTimerInterval = 8; + static const u32 kTimeCheckMask = ~(kTimerInterval - 1); + + bool Enabled; + bool PowerOn; + + s32 TimerError; + + u16 Random; + + // general, always-on microsecond counter + u64 USTimestamp; + + u64 USCounter; + u64 USCompare; + bool BlockBeaconIRQ14; + + u32 CmdCounter; + + u8 BBRegs[0x100]; + u8 BBRegsRO[0x100]; + + u8 RFVersion; + u32 RFRegs[0x40]; + + u32 RFChannelIndex[2]; + u32 RFChannelData[14][2]; + int CurChannel; + + struct TXSlot + { + bool Valid; + u16 Addr; + u16 Length; + u8 Rate; + u8 CurPhase; + int CurPhaseTime; + u32 HalfwordTimeMask; + }; + + TXSlot TXSlots[6]; + u8 TXBuffer[0x2000]; + + u8 RXBuffer[2048]; + u32 RXBufferPtr; + int RXTime; + u32 RXHalfwordTimeMask; + + u32 ComStatus; // 0=waiting for packets 1=receiving 2=sending + u32 TXCurSlot; + u32 RXCounter; + + int MPReplyTimer; + u16 MPClientMask, MPClientFail; + + u8 MPClientReplies[15*1024]; + + u16 MPLastSeqno; + + bool MPInited; + bool LANInited; + + int USUntilPowerOn; + + // MULTIPLAYER SYNC APPARATUS + bool IsMP; + bool IsMPClient; + u64 NextSync; // for clients: timestamp for next sync point + u64 RXTimestamp; + + class WifiAP* WifiAP; + + void ScheduleTimer(bool first); + void UpdatePowerOn(); + + void CheckIRQ(u16 oldflags); + void SetIRQ(u32 irq); + void SetIRQ13(); + void SetIRQ14(int source); + void SetIRQ15(); + + void SetStatus(u32 status); + + void UpdatePowerStatus(int power); + + int PreambleLen(int rate) const; + u32 NumClients(u16 bitmask) const; + void IncrementTXCount(const TXSlot* slot); + void ReportMPReplyErrors(u16 clientfail); + + void TXSendFrame(const TXSlot* slot, int num); + void StartTX_LocN(int nslot, int loc); + void StartTX_Cmd(); + void StartTX_Beacon(); + void FireTX(); + void SendMPDefaultReply(); + void SendMPReply(u16 clienttime, u16 clientmask); + void SendMPAck(u16 cmdcount, u16 clientfail); + bool ProcessTX(TXSlot* slot, int num); + + void IncrementRXAddr(u16& addr, u16 inc = 2); + void StartRX(); + void FinishRX(); + void MPClientReplyRX(int client); + bool CheckRX(int type); + + void MSTimer(); + + void ChangeChannel(); + + void RFTransfer_Type2(); + void RFTransfer_Type3(); }; -enum -{ - Event_RXCheck = 0, - Event_IRQ15, - Event_MSTimer, - Event_RFWakeup, - Event_RX, - Event_TX, - Event_MPClientSync, - Event_RF, - Event_BB, - - Event_MAX -}; - -struct SchedEvent -{ - void (*Func)(u32 param); - u64 Timestamp; - u32 Param; -}; - - -extern bool MPInited; - - -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); - -void SetPowerCnt(u32 val); - -void USTimer(u32 param); - -u16 Read(u32 addr); -void Write(u32 addr, u16 val); - -u8* GetMAC(); -u8* GetBSSID(); - } - #endif diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 9fef220d..855dc244 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -27,13 +27,15 @@ #include #endif +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -namespace WifiAP -{ -const u8 APMac[6] = {AP_MAC}; +const char* WifiAP::APName = "melonAP"; +const u8 WifiAP::APMac[6] = {0x00, 0xF0, 0x77, 0x77, 0x77, 0x77}; +const u8 WifiAP::APChannel = 6; #define PWRITE_8(p, v) *p++ = v; #define PWRITE_16(p, v) *(u16*)p = v; p += 2; @@ -54,7 +56,7 @@ const u8 APMac[6] = {AP_MAC}; PWRITE_16(p, 0); \ PWRITE_16(p, 0); \ PWRITE_8(p, rate); \ - PWRITE_8(p, 0); \ + PWRITE_8(p, APChannel); \ PWRITE_16(p, len); //#define PALIGN_4(p, base) p += ((4 - ((ptrdiff_t)(p-base) & 0x3)) & 0x3); @@ -65,33 +67,19 @@ const u8 APMac[6] = {AP_MAC}; #define PALIGN_4(p, base) while (PLEN(p,base) & 0x3) *p++ = 0xFF; -u64 USCounter; - -u16 SeqNo; - -bool BeaconDue; - -u8 PacketBuffer[2048]; -int PacketLen; -int RXNum; - -u8 LANBuffer[2048]; - -// this is a lazy AP, we only keep track of one client -// 0=disconnected 1=authenticated 2=associated -int ClientStatus; +bool MACEqual(const u8* a, const u8* b); +bool MACIsBroadcast(const u8* a); -bool Init() -{ - return true; -} - -void DeInit() +WifiAP::WifiAP(Wifi* client) : Client(client) { } -void Reset() +WifiAP::~WifiAP() +{ +} + +void WifiAP::Reset() { // random starting point for the counter USCounter = 0x428888000ULL; @@ -107,18 +95,7 @@ void Reset() } -bool MACEqual(u8* a, u8* b) -{ - return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]); -} - -bool MACIsBroadcast(u8* a) -{ - return (*(u32*)&a[0] == 0xFFFFFFFF) && (*(u16*)&a[4] == 0xFFFF); -} - - -void MSTimer() +void WifiAP::MSTimer() { USCounter += 0x400; @@ -131,7 +108,7 @@ void MSTimer() } -int HandleManagementFrame(u8* data, int len) +int WifiAP::HandleManagementFrame(const u8* data, int len) { // TODO: perfect this // noting that frames sent pre-auth/assoc don't have a proper BSSID @@ -198,9 +175,9 @@ int HandleManagementFrame(u8* data, int len) PWRITE_16(p, 128); // beacon interval PWRITE_16(p, 0x0021); // capability PWRITE_8(p, 0x01); PWRITE_8(p, 0x02); PWRITE_8(p, 0x82); PWRITE_8(p, 0x84); // rates - PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, 0x06); // current channel - PWRITE_8(p, 0x00); PWRITE_8(p, strlen(AP_NAME)); - memcpy(p, AP_NAME, strlen(AP_NAME)); p += strlen(AP_NAME); + PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, APChannel); // current channel + PWRITE_8(p, 0x00); PWRITE_8(p, strlen(APName)); + memcpy(p, APName, strlen(APName)); p += strlen(APName); PacketLen = PLEN(p, base); RXNum = 1; @@ -282,8 +259,11 @@ int HandleManagementFrame(u8* data, int len) } -int SendPacket(u8* data, int len) +int WifiAP::SendPacket(const u8* data, int len) { + if (data[9] != APChannel) + return 0; + data += 12; u16 framectl = *(u16*)&data[0]; @@ -330,7 +310,7 @@ int SendPacket(u8* data, int len) return 0; } -int RecvPacket(u8* data) +int WifiAP::RecvPacket(u8* data) { if (BeaconDue) { @@ -351,10 +331,10 @@ int RecvPacket(u8* data) PWRITE_16(p, 128); // beacon interval PWRITE_16(p, 0x0021); // capability PWRITE_8(p, 0x01); PWRITE_8(p, 0x02); PWRITE_8(p, 0x82); PWRITE_8(p, 0x84); // rates - PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, 0x06); // current channel + PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, APChannel); // current channel PWRITE_8(p, 0x05); PWRITE_8(p, 0x04); PWRITE_8(p, 0); PWRITE_8(p, 0); PWRITE_8(p, 0); PWRITE_8(p, 0); // TIM - PWRITE_8(p, 0x00); PWRITE_8(p, strlen(AP_NAME)); - memcpy(p, AP_NAME, strlen(AP_NAME)); p += strlen(AP_NAME); + PWRITE_8(p, 0x00); PWRITE_8(p, strlen(APName)); + memcpy(p, APName, strlen(APName)); p += strlen(APName); PALIGN_4(p, base); PWRITE_32(p, 0xDEADBEEF); // checksum. doesn't matter for now @@ -394,7 +374,7 @@ int RecvPacket(u8* data) // check destination MAC if (!MACIsBroadcast(&LANBuffer[0])) { - if (!MACEqual(&LANBuffer[0], Wifi::GetMAC())) + if (!MACEqual(&LANBuffer[0], Client->GetMAC())) return 0; } @@ -428,4 +408,4 @@ int RecvPacket(u8* data) return 0; } -} +} \ No newline at end of file diff --git a/src/WifiAP.h b/src/WifiAP.h index e5ca1ed7..a9e80c3b 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,24 +21,48 @@ #include "types.h" -namespace WifiAP +namespace melonDS { +class Wifi; -#define AP_MAC 0x00, 0xF0, 0x77, 0x77, 0x77, 0x77 -#define AP_NAME "melonAP" +class WifiAP +{ +public: + WifiAP(Wifi* client); + ~WifiAP(); + void Reset(); -extern const u8 APMac[6]; + static const char* APName; + static const u8 APMac[6]; + static const u8 APChannel; -bool Init(); -void DeInit(); -void Reset(); + void MSTimer(); -void MSTimer(); + // packet format: 12-byte TX header + original 802.11 frame + int SendPacket(const u8* data, int len); + int RecvPacket(u8* data); -// packet format: 12-byte TX header + original 802.11 frame -int SendPacket(u8* data, int len); -int RecvPacket(u8* data); +private: + Wifi* Client; + + u64 USCounter; + + u16 SeqNo; + + bool BeaconDue; + + u8 PacketBuffer[2048]; + int PacketLen; + int RXNum; + + u8 LANBuffer[2048]; + + // this is a lazy AP, we only keep track of one client + // 0=disconnected 1=authenticated 2=associated + int ClientStatus; + + int HandleManagementFrame(const u8* data, int len); +}; } - #endif diff --git a/src/debug/GdbArch.h b/src/debug/GdbArch.h new file mode 100644 index 00000000..1d45fb0b --- /dev/null +++ b/src/debug/GdbArch.h @@ -0,0 +1,63 @@ + +#ifndef GDBARCH_H_ +#define GDBARCH_H_ + +namespace Gdb +{ + +using namespace melonDS; +enum class Register : int +{ + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + sp, + lr, + pc, + + cpsr, + sp_usr, + lr_usr, + + r8_fiq, + r9_fiq, + r10_fiq, + r11_fiq, + r12_fiq, + + sp_fiq, + lr_fiq, + sp_irq, + lr_irq, + sp_svc, + lr_svc, + sp_abt, + lr_abt, + sp_und, + lr_und, + + spsr_fiq, + spsr_irq, + spsr_svc, + spsr_abt, + spsr_und, + + COUNT +}; + +constexpr int GDB_ARCH_N_REG = (int)Register::COUNT; + +} + +#endif + diff --git a/src/debug/GdbCmds.cpp b/src/debug/GdbCmds.cpp new file mode 100644 index 00000000..c4706138 --- /dev/null +++ b/src/debug/GdbCmds.cpp @@ -0,0 +1,925 @@ + +#include +#include + +#include "../CRC32.h" +#include "../Platform.h" +#include "hexutil.h" + +#include "GdbProto.h" + +using namespace melonDS; +using Platform::Log; +using Platform::LogLevel; + +namespace Gdb +{ + +enum class GdbSignal : int +{ + INT = 2, + TRAP = 5, + EMT = 7, // "emulation trap" + SEGV = 11, + ILL = 4 +}; + +// 12: llvm::MachO::CPU_TYPE_ARM +// 5: llvm::MachO::CPU_SUBTYPE_ARM_V4T +// 7: llvm::MachO::CPU_SUBTYPE_ARM_V5TEJ +const char* TARGET_INFO_ARM7 = "cputype:12;cpusubtype:5;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;"; +const char* TARGET_INFO_ARM9 = "cputype:12;cpusubtype:7;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;"; + + +#define TARGET_XML__CORE_REGS \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + /* 16 regs */ \ + +#define TARGET_XML__MODE_REGS \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + /* 23 regs */ \ + + +const char* TARGET_XML_ARM7 = + "" + "armv4t" + "none" + "" + TARGET_XML__CORE_REGS + TARGET_XML__MODE_REGS + // 39 regs total + "" + ""; + + +const char* TARGET_XML_ARM9 = + "" + "armv5te" + "none" + "" + TARGET_XML__CORE_REGS + TARGET_XML__MODE_REGS + // 39 regs total + "" + ""; + // TODO: CP15? + + +static int DoQResponse(GdbStub* stub, const u8* query, const char* data, const size_t len) +{ + size_t qaddr, qlen; + + Log(LogLevel::Debug, "[GDB qresp] query='%s'\n", query); + if (sscanf((const char*)query, "%zx,%zx", &qaddr, &qlen) != 2) + { + return stub->RespStr("E01"); + } + else if (qaddr > len) + { + return stub->RespStr("E01"); + } + else if (qaddr == len) + { + return stub->RespStr("l"); + } + + size_t bleft = len - qaddr; + size_t outlen = qlen; + if (outlen > bleft) outlen = bleft; + Log(LogLevel::Debug, "[GDB qresp] qaddr=%zu qlen=%zu left=%zu outlen=%zu\n", + qaddr, qlen, bleft, outlen); + + return stub->RespC("m", 1, (const u8*)&data[qaddr], outlen); +} + +__attribute__((__aligned__(4))) +static u8 tempdatabuf[1024]; + +ExecResult GdbStub::Handle_g(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u8* regstrbuf = tempdatabuf; + + for (size_t i = 0; i < GDB_ARCH_N_REG; ++i) + { + u32 v = stub->Cb->ReadReg(static_cast(i)); + hexfmt32(®strbuf[i*4*2], v); + } + + stub->Resp(regstrbuf, GDB_ARCH_N_REG*4*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_G(GdbStub* stub, const u8* cmd, ssize_t len) +{ + if (len != GDB_ARCH_N_REG*4*2) + { + Log(LogLevel::Error, "[GDB] REG WRITE ERR: BAD LEN: %zd != %d!\n", len, GDB_ARCH_N_REG*4*2); + stub->RespStr("E01"); + return ExecResult::Ok; + } + + for (int i = 0; i < GDB_ARCH_N_REG; ++i) + { + u32 v = unhex32(&cmd[i*4*2]); + stub->Cb->WriteReg(static_cast(i), v); + } + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_m(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr = 0, llen = 0, end; + + if (sscanf((const char*)cmd, "%08X,%08X", &addr, &llen) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + u8* datastr = tempdatabuf; + u8* dataptr = datastr; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u32 v = stub->Cb->ReadMem(addr, 16); + hexfmt16(dataptr, v&0xffff); + addr += 2; + dataptr += 4; + } + else if ((end-addr) == 1) + { // last byte + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = stub->Cb->ReadMem(addr, 32); + hexfmt32(dataptr, v); + addr += 4; + dataptr += 8; + } + + // post-align: short + if ((end-addr) & 2) + { + u32 v = stub->Cb->ReadMem(addr, 16); + hexfmt16(dataptr, v&0xffff); + addr += 2; + dataptr += 4; + } + + // post-align: byte + if ((end-addr) == 1) + { + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + +end: + assert(addr == end); + + stub->Resp(datastr, llen*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_M(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr, llen, end; + int inoff; + + if (sscanf((const char*)cmd, "%08X,%08X:%n", &addr, &llen, &inoff) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + const u8* dataptr = cmd + inoff; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u16 v = unhex16(dataptr); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 4; + } + else if ((end-addr) == 1) + { // last byte + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = unhex32(dataptr); + stub->Cb->WriteMem(addr, 32, v); + addr += 4; + dataptr += 8; + } + + // post-align: short + if ((end-addr) & 2) + { + u16 v = unhex16(dataptr); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 4; + } + + // post-align: byte + if ((end-addr) == 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + +end: + assert(addr == end); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_X(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr, llen, end; + int inoff; + + if (sscanf((const char*)cmd, "%08X,%08X:%n", &addr, &llen, &inoff) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + const u8* dataptr = cmd + inoff; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u8 v = *dataptr; + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u16 v = dataptr[0] | ((u16)dataptr[1] << 8); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 2; + } + else if ((end-addr) == 1) + { // last byte + u8 v = *dataptr; + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = dataptr[0] | ((u32)dataptr[1] << 8) + | ((u32)dataptr[2] << 16) | ((u32)dataptr[3] << 24); + stub->Cb->WriteMem(addr, 32, v); + addr += 4; + dataptr += 4; + } + + // post-align: short + if ((end-addr) & 2) + { + u16 v = dataptr[0] | ((u16)dataptr[1] << 8); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 2; + } + + // post-align: byte + if ((end-addr) == 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + +end: + assert(addr == end); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_c(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr = ~(u32)0; + + if (len > 0) + { + if (len <= 8) + { + if (sscanf((const char*)cmd, "%08X", &addr) != 1) + { + stub->RespStr("E01"); + } // else: ok + } + else + { + stub->RespStr("E01"); + } + } // else: continue at current + + if (~addr) + { + stub->Cb->WriteReg(Register::pc, addr); + } + + return ExecResult::Continue; +} + +ExecResult GdbStub::Handle_s(GdbStub* stub, const u8* cmd, ssize_t len) { + u32 addr = ~(u32)0; + + if (len > 0) + { + if (len <= 8) + { + if (sscanf((const char*)cmd, "%08X", &addr) != 1) { + stub->RespStr("E01"); + return ExecResult::Ok; + } // else: ok + } + else + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + } // else: continue at current + + if (~addr != 0) + { + stub->Cb->WriteReg(Register::pc, addr); + } + + return ExecResult::Step; +} + +ExecResult GdbStub::Handle_p(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int reg; + if (sscanf((const char*)cmd, "%x", ®) != 1 || reg < 0 || reg >= GDB_ARCH_N_REG) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 v = stub->Cb->ReadReg(static_cast(reg)); + hexfmt32(tempdatabuf, v); + stub->Resp(tempdatabuf, 4*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_P(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int reg, dataoff; + + if (sscanf((const char*)cmd, "%x=%n", ®, &dataoff) != 1 || reg < 0 + || reg >= GDB_ARCH_N_REG || dataoff + 4*2 > len) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 v = unhex32(&cmd[dataoff]); + stub->Cb->WriteReg(static_cast(reg), v); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_H(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u8 operation = cmd[0]; + u32 thread_id; + sscanf((const char*)&cmd[1], "%u", &thread_id); + + (void)operation; + if (thread_id <= 1) + { + stub->RespStr("OK"); + } + else + { + stub->RespStr("E01"); + } + + return ExecResult::Ok; +} + + +ExecResult GdbStub::Handle_Question(GdbStub* stub, const u8* cmd, ssize_t len) +{ + // "request reason for target halt" (which must also halt) + + TgtStatus st = stub->Stat; + u32 arg = ~(u32)0; + int typ = 0; + + switch (st) + { + case TgtStatus::None: // no target! + stub->RespStr("W00"); + break; + + case TgtStatus::Running: // will break very soon due to retval + case TgtStatus::BreakReq: + stub->RespFmt("S%02X", GdbSignal::INT); + break; + + case TgtStatus::SingleStep: + stub->RespFmt("S%02X", GdbSignal::TRAP); + break; + + case TgtStatus::Bkpt: + arg = stub->CurBkpt; + typ = 1; + goto bkpt_rest; + case TgtStatus::Watchpt: + arg = stub->CurWatchpt; + typ = 2; + bkpt_rest: + if (!~arg) + { + stub->RespFmt("S%02X", GdbSignal::TRAP); + } + else + { + switch (typ) + { + case 1: + stub->RespFmt("S%02X", GdbSignal::TRAP); + //stub->RespFmt("T%02Xhwbreak:"/*"%08X"*/";", GdbSignal::TRAP/*, arg*/); + break; + case 2: + stub->RespFmt("S%02X", GdbSignal::TRAP); + //stub->RespFmt("T%02Xwatch:"/*"%08X"*/";", GdbSignal::TRAP/*, arg*/); + break; + default: + stub->RespFmt("S%02X", GdbSignal::TRAP); + break; + } + } + break; + case TgtStatus::BkptInsn: + stub->RespFmt("T%02Xswbreak:%08X;", GdbSignal::TRAP, + stub->Cb->ReadReg(Register::pc)); + break; + + // these three should technically be a SIGBUS but gdb etc don't really + // like that (plus it sounds confusing) + case TgtStatus::FaultData: + case TgtStatus::FaultIAcc: + stub->RespFmt("S%02X", GdbSignal::SEGV); + break; + case TgtStatus::FaultInsn: + stub->RespFmt("S%02X", GdbSignal::ILL); + break; + default: break; + } + + return ExecResult::InitialBreak; +} + +ExecResult GdbStub::Handle_Exclamation(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_D(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("OK"); + return ExecResult::Detached; +} + +ExecResult GdbStub::Handle_r(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Cb->ResetGdb(); + return ExecResult::Ok; +} +ExecResult GdbStub::Handle_R(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Cb->ResetGdb(); + return ExecResult::Ok; +} +ExecResult GdbStub::Handle_k(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return ExecResult::Detached; +} + + +ExecResult GdbStub::Handle_z(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int typ; + u32 addr, kind; + + if (sscanf((const char*)cmd, "%d,%x,%u", &typ, &addr, &kind) != 3) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (typ) + { + case 0: case 1: // remove breakpoint (we cheat & always insert a hardware breakpoint) + stub->DelBkpt(addr, kind); + break; + case 2: case 3: case 4: // watchpoint. currently not distinguishing between reads & writes oops + stub->DelWatchpt(addr, kind, typ); + break; + default: + stub->RespStr("E02"); + return ExecResult::Ok; + } + + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_Z(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int typ; + u32 addr, kind; + + if (sscanf((const char*)cmd, "%d,%x,%u", &typ, &addr, &kind) != 3) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (typ) + { + case 0: case 1: // insert breakpoint (we cheat & always insert a hardware breakpoint) + stub->AddBkpt(addr, kind); + break; + case 2: case 3: case 4: // watchpoint. currently not distinguishing between reads & writes oops + stub->AddWatchpt(addr, kind, typ); + break; + default: + stub->RespStr("E02"); + return ExecResult::Ok; + } + + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_HostInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + const char* resp = ""; + + switch (stub->Cb->GetCPU()) + { + case 7: resp = TARGET_INFO_ARM7; break; + case 9: resp = TARGET_INFO_ARM9; break; + default: break; + } + + stub->RespStr(resp); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Rcmd(GdbStub* stub, const u8* cmd, ssize_t len) +{ + + memset(tempdatabuf, 0, sizeof tempdatabuf); + for (ssize_t i = 0; i < len/2; ++i) + { + tempdatabuf[i] = unhex8(&cmd[i*2]); + } + + int r = stub->Cb->RemoteCmd(tempdatabuf, len/2); + + if (r) stub->RespFmt("E%02X", r&0xff); + else stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Supported(GdbStub* stub, + const u8* cmd, ssize_t len) { + // TODO: support Xfer:memory-map:read:: + // but NWRAM is super annoying with that + stub->RespFmt("PacketSize=%X;qXfer:features:read+;swbreak-;hwbreak+;QStartNoAckMode+", GDBPROTO_BUFFER_CAPACITY-1); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_CRC(GdbStub* stub, + const u8* cmd, ssize_t llen) +{ + static u8 crcbuf[128]; + + u32 addr, len; + if (sscanf((const char*)cmd, "%x,%x", &addr, &len) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 val = 0; // start at 0 + u32 caddr = addr; + u32 realend = addr + len; + + for (; caddr < addr + len; ) + { + // calc partial CRC in 128-byte chunks + u32 end = caddr + sizeof(crcbuf)/sizeof(crcbuf[0]); + if (end > realend) end = realend; + u32 clen = end - caddr; + + for (size_t i = 0; caddr < end; ++caddr, ++i) + { + crcbuf[i] = stub->Cb->ReadMem(caddr, 8); + } + + val = CRC32(crcbuf, clen, val); + } + + stub->RespFmt("C%x", val); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_C(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("QC1"); // current thread ID is 1 + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_fThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("m1"); // one thread + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_sThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("l"); // end of thread list + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_features(GdbStub* stub, const u8* cmd, ssize_t len) +{ + const char* resp; + + Log(LogLevel::Debug, "[GDB] CPU type = %d\n", stub->Cb->GetCPU()); + switch (stub->Cb->GetCPU()) + { + case 7: resp = TARGET_XML_ARM7; break; + case 9: resp = TARGET_XML_ARM9; break; + default: resp = ""; break; + } + + DoQResponse(stub, cmd, resp, strlen(resp)); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Attached(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("1"); // always "attach to a process" + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Attach(GdbStub* stub, const u8* cmd, ssize_t len) +{ + + TgtStatus st = stub->Stat; + + if (st == TgtStatus::None) + { + // no target + stub->RespStr("E01"); + return ExecResult::Ok; + } + + stub->RespStr("T05thread:1;"); + + if (st == TgtStatus::Running) return ExecResult::MustBreak; + else return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Kill(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + stub->Cb->ResetGdb(); + + stub->RespStr("OK"); + + return (st != TgtStatus::Running && st != TgtStatus::None) ? ExecResult::Detached : ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Run(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + stub->Cb->ResetGdb(); + + // TODO: handle cmdline for homebrew? + + return (st != TgtStatus::Running && st != TgtStatus::None) ? ExecResult::Continue : ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + static bool notified = true; + + // not sure if i understand this correctly + if (st != TgtStatus::Running) + { + if (notified) stub->RespStr("OK"); + else stub->RespStr("W00"); + + notified = !notified; + } + else stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Resp(NULL, 0); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len) +{ + if (len < 1) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (cmd[0]) + { + case 'c': + stub->RespStr("OK"); + return ExecResult::Continue; + case 's': + stub->RespStr("OK"); + return ExecResult::Step; + case 't': + stub->RespStr("OK"); + return ExecResult::MustBreak; + default: + stub->RespStr("E01"); + return ExecResult::Ok; + } +} + +ExecResult GdbStub::Handle_v_ContQuery(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("vCont;c;s;t"); + return ExecResult::Ok; +} + + +ExecResult GdbStub::Handle_Q_StartNoAckMode(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->NoAck = true; + stub->RespStr("OK"); + return ExecResult::Ok; +} + +} + diff --git a/src/debug/GdbCmds.h b/src/debug/GdbCmds.h new file mode 100644 index 00000000..4f300606 --- /dev/null +++ b/src/debug/GdbCmds.h @@ -0,0 +1,53 @@ + +#ifndef GDBSTUB_H_ +#error "DO NOT INCLUDE THIS FILE YOURSELF!" +#endif + +private: + static ExecResult Handle_g(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_G(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_m(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_M(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_X(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_c(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_s(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_p(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_P(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_H(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_Question(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_Exclamation(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_D(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_r(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_R(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_k(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_z(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_Z(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_q(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_Q(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_q_HostInfo(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_Rcmd(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_Supported(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_CRC(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_C(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_fThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_sThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_TStatus(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_features(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_Attached(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_v_Attach(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Kill(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Run(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_ContQuery(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_Q_StartNoAckMode(GdbStub* stub, const u8* cmd, ssize_t len); + diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp new file mode 100644 index 00000000..ebdf3be5 --- /dev/null +++ b/src/debug/GdbProto.cpp @@ -0,0 +1,390 @@ + +#ifdef _WIN32 +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#endif + +#include "../Platform.h" +#include "hexutil.h" + +#include "GdbProto.h" + + +using namespace melonDS; +using Platform::Log; +using Platform::LogLevel; + +namespace Gdb +{ + +/* + * TODO commands to support: + * m M g G c s p P H + * ? D r + * qC qfThreadInfo qsThreadInfo + * z0 Z0 z1 Z1 z4 Z4 + * qCRC + * vAttach;addr + * vKill;pid + * qRcmd? qSupported? + */ +u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; +ssize_t Cmdlen; + +namespace Proto +{ + +u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; +u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; + +ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]) +{ + static ssize_t dataoff = 0; + + ssize_t recv_total = dataoff; + ssize_t cksumoff = -1; + u8 sum = 0; + + bool first = true; + + //printf("--- dataoff=%zd\n", dataoff); + if (dataoff != 0) { + printf("--- got preexisting: %s\n", PacketBuf); + + ssize_t datastart = 0; + while (true) + { + if (PacketBuf[datastart] == '\x04') return ReadResult::Eof; + else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-') + { + /*if (PacketBuf[datastart] == '+') SendAck(connfd); + else SendNak(connfd);*/ + ++datastart; + continue; + } + else if (PacketBuf[datastart] == '$') + { + ++datastart; + break; + } + else + { + __builtin_trap(); + return ReadResult::Wut; + } + } + printf("--- datastart=%zd\n", datastart); + + for (ssize_t i = datastart; i < dataoff; ++i) + { + if (PacketBuf[i] == '#') + { + cksumoff = i + 1; + printf("--- cksumoff=%zd\n", cksumoff); + break; + } + + sum += PacketBuf[i]; + } + + if (cksumoff >= 0) + { + recv_total = dataoff - datastart + 1; + dataoff = cksumoff + 2 - datastart + 1; + cksumoff -= datastart - 1; + + memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total); + PacketBuf[0] = '$'; + PacketBuf[recv_total] = 0; + + printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n", + cksumoff, recv_total, datastart, dataoff, PacketBuf); + //break; + } + } + + while (cksumoff < 0) + { + u8* pkt = &PacketBuf[dataoff]; + ssize_t n, blehoff = 0; + + memset(pkt, 0, sizeof(PacketBuf) - dataoff); + int flag = 0; +#if MOCKTEST + static bool FIRST = false; + if (FIRST) { + printf("%s", "[==>] TEST DONE\n"); + __builtin_trap(); + } + FIRST = true; + + const char* testinp1 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77"; + const char* testinp2 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77---+$vMustReplyEmpty#3a"; + + const char* testinp = testinp1; + + n = strlen(testinp); + memcpy(pkt, testinp, strlen(testinp)); +#else +#ifndef _WIN32 + if (first) flag |= MSG_DONTWAIT; + n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag); +#else + // fuck windows + n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag); +#endif +#endif + + if (n <= 0) + { + if (first) return ReadResult::NoPacket; + else + { + Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno)); + return ReadResult::Eof; + } + } + + Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]); + first = false; + + do + { + if (dataoff == 0) + { + if (pkt[blehoff] == '\x04') return ReadResult::Eof; + else if (pkt[blehoff] == '\x03') return ReadResult::Break; + else if (pkt[blehoff] != '$') + { + ++blehoff; + --n; + } + else break; + + if (n == 0) goto next_outer; + } + } + while (true); + + if (blehoff > 0) + { + memmove(pkt, &pkt[blehoff], n - blehoff + 1); + n -= blehoff - 1; // ??? + } + + recv_total += n; + + Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total); + + for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i) + { + u8 v = pkt[i]; + if (v == '#') + { + cksumoff = dataoff + i + 1; + break; + } + + sum += pkt[i]; + } + + if (cksumoff < 0) + { + // oops, need more data + dataoff += n; + } + + next_outer:; + } + + u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4) + | hex2nyb(PacketBuf[cksumoff+1]); + + Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum); + + if (ck != sum) + { + //__builtin_trap(); + return ReadResult::CksumErr; + } + + if (cksumoff + 2 > recv_total) + { + Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total); + //__builtin_trap(); + return ReadResult::Wut; + } + else + { + Cmdlen = cksumoff - 2; + memcpy(Cmdbuf, &PacketBuf[1], Cmdlen); + Cmdbuf[Cmdlen] = 0; + + if (cksumoff + 2 < recv_total) { + // huh, we have the start of the next packet + dataoff = recv_total - (cksumoff + 2); + memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff); + PacketBuf[dataoff] = 0; + Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf); + } + else dataoff = 0; + } + + return ReadResult::CmdRecvd; +} + +int SendAck(int connfd) +{ + Log(LogLevel::Debug, "[GDB] send ack\n"); + u8 v = '+'; +#if MOCKTEST + return 1; +#endif + +#ifdef _WIN32 + // fuck windows + return send(connfd, (const char*)&v, 1, 0); +#else + return send(connfd, &v, 1, 0); +#endif +} + +int SendNak(int connfd) +{ + Log(LogLevel::Debug, "[GDB] send nak\n"); + u8 v = '-'; +#if MOCKTEST + return 1; +#endif + +#ifdef _WIN32 + // fuck windows + return send(connfd, (const char*)&v, 1, 0); +#else + return send(connfd, &v, 1, 0); +#endif +} + +int WaitAckBlocking(int connfd, u8* ackp, int to_ms) +{ +#if MOCKTEST + *ackp = '+'; + return 0; +#endif + +#ifdef _WIN32 + fd_set infd, outfd, errfd; + FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); + FD_SET(connfd, &infd); + + struct timeval to; + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms % 1000) * 1000; + + int r = select(connfd+1, &infd, &outfd, &errfd, &to); + + if (FD_ISSET(connfd, &errfd)) return -1; + else if (FD_ISSET(connfd, &infd)) + { + r = recv(connfd, (char*)ackp, 1, 0); + if (r < 0) return r; + return 0; + } + + return -1; +#else + struct pollfd pfd; + + pfd.fd = connfd; + pfd.events = POLLIN; + pfd.revents = 0; + + ssize_t r = (ssize_t)poll(&pfd, 1, to_ms); + if (r < 0) return r; + if (r == 0) return -1; + + if (pfd.revents & (POLLHUP|POLLERR)) return -69; + + r = recv(connfd, ackp, 1, 0); + if (r < 0) return r; + + return (r == 1) ? 0 : -1; +#endif +} + +int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack) +{ + u8 cksum = 0; + int tries = 0; + + size_t totallen = len1 + len2; + + if (totallen >= GDBPROTO_BUFFER_CAPACITY) + { + Log(LogLevel::Error, "[GDB] packet with len %zu can't fit in buffer!\n", totallen); + return -42; + } + + RespBuf[0] = '$'; + for (size_t i = 0; i < len1; ++i) + { + cksum += data1[i]; + RespBuf[i+1] = data1[i]; + } + for (size_t i = 0; i < len2; ++i) + { + cksum += data2[i]; + RespBuf[len1+i+1] = data2[i]; + } + RespBuf[totallen+1] = '#'; + hexfmt8(&RespBuf[totallen+2], cksum); + RespBuf[totallen+4] = 0; + + do + { + ssize_t r; + u8 ack; + + Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf); +#if MOCKTEST + r = totallen+4; +#else +#ifdef _WIN32 + r = send(connfd, (const char*)RespBuf, totallen+4, 0); +#else + r = send(connfd, RespBuf, totallen+4, 0); +#endif +#endif + if (r < 0) return r; + + if (noack) break; + + r = WaitAckBlocking(connfd, &ack, 2000); + //Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack); + if (r == 0 && ack == '+') break; + + ++tries; + } + while (tries < 3); + + return 0; +} + +} + +} + diff --git a/src/debug/GdbProto.h b/src/debug/GdbProto.h new file mode 100644 index 00000000..68122f06 --- /dev/null +++ b/src/debug/GdbProto.h @@ -0,0 +1,41 @@ + +#ifndef GDBPROTO_H_ +#define GDBPROTO_H_ + +#include +#include + +#include "GdbStub.h" /* class GdbStub */ + + +#define MOCKTEST 0 + + +namespace Gdb { + +using namespace melonDS; +constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128; + +extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; +extern ssize_t Cmdlen; + +namespace Proto { + +extern u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; +extern u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; + +Gdb::ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]); + +int SendAck(int connfd); +int SendNak(int connfd); + +int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack); + +int WaitAckBlocking(int connfd, u8* ackp, int to_ms); + +} + +} + +#endif + diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp new file mode 100644 index 00000000..099f27f1 --- /dev/null +++ b/src/debug/GdbStub.cpp @@ -0,0 +1,694 @@ + +#ifdef _WIN32 +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#endif + + +#include "../Platform.h" +#include "GdbProto.h" + +using namespace melonDS; +using Platform::Log; +using Platform::LogLevel; + +static int SocketSetBlocking(int fd, bool block) +{ +#if MOCKTEST + return 0; +#endif + + if (fd < 0) return -1; + +#ifdef _WIN32 + unsigned long mode = block ? 0 : 1; + return ioctlsocket(fd, FIONBIO, &mode); +#else + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) return -1; + flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + return fcntl(fd, F_SETFL, flags); +#endif +} + +namespace Gdb +{ + +GdbStub::GdbStub(StubCallbacks* cb, int port) + : Cb(cb), Port(port) + , SockFd(0), ConnFd(0) + , Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false) + , ServerSA((void*)new struct sockaddr_in()) + , ClientSA((void*)new struct sockaddr_in()) +{ } + +bool GdbStub::Init() +{ + Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n", + Cb->GetCPU(), Port); + +#if MOCKTEST + SockFd = 0; + return true; +#endif + +#ifndef _WIN32 + /*void* fn = SIG_IGN; + struct sigaction act = { 0 }; + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = (sighandler_t)fn; + if (sigaction(SIGPIPE, &act, NULL) == -1) { + Log(LogLevel::Warn, "[GDB] couldn't ignore SIGPIPE, stuff may fail on GDB disconnect.\n"); + }*/ + signal(SIGPIPE, SIG_IGN); +#else + WSADATA wsa; + if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) + { + Log(LogLevel::Error, "[GDB] winsock could not be initialized (%d).\n", WSAGetLastError()); + return false; + } +#endif + + int r; + struct sockaddr_in* server = (struct sockaddr_in*)ServerSA; + struct sockaddr_in* client = (struct sockaddr_in*)ClientSA; + + int typ = SOCK_STREAM; +#ifdef __linux__ + typ |= SOCK_NONBLOCK; +#endif + SockFd = socket(AF_INET, SOCK_STREAM, 0); + if (SockFd < 0) + { + Log(LogLevel::Error, "[GDB] err: can't create a socket fd\n"); + goto err; + } +#ifndef __linux__ + SocketSetBlocking(SockFd, false); +#endif + + server->sin_family = AF_INET; + server->sin_addr.s_addr = htonl(INADDR_ANY); + server->sin_port = htons(Port); + + r = bind(SockFd, (const sockaddr*)server, sizeof(*server)); + if (r < 0) + { + Log(LogLevel::Error, "[GDB] err: can't bind to address and port %d\n", Port); + goto err; + } + + r = listen(SockFd, 5); + if (r < 0) + { + Log(LogLevel::Error, "[GDB] err: can't listen to SockFd\n"); + goto err; + } + + return true; + +err: + if (SockFd != 0) + { +#ifdef _WIN32 + closesocket(SockFd); +#else + close(SockFd); +#endif + SockFd = 0; + } + + return false; +} + +void GdbStub::Close() +{ + Disconnect(); + if (SockFd > 0) close(SockFd); + SockFd = 0; +} + +void GdbStub::Disconnect() +{ + if (ConnFd > 0) close(ConnFd); + ConnFd = 0; +} + +GdbStub::~GdbStub() +{ + Close(); + delete (struct sockaddr_in*)ServerSA; + delete (struct sockaddr_in*)ClientSA; +} + +SubcmdHandler GdbStub::Handlers_v[] = { + { .MainCmd = 'v', .SubStr = "Attach;" , .Handler = GdbStub::Handle_v_Attach }, + { .MainCmd = 'v', .SubStr = "Kill;" , .Handler = GdbStub::Handle_v_Kill }, + { .MainCmd = 'v', .SubStr = "Run" , .Handler = GdbStub::Handle_v_Run }, + { .MainCmd = 'v', .SubStr = "Stopped" , .Handler = GdbStub::Handle_v_Stopped }, + { .MainCmd = 'v', .SubStr = "MustReplyEmpty", .Handler = GdbStub::Handle_v_MustReplyEmpty }, + { .MainCmd = 'v', .SubStr = "Cont?" , .Handler = GdbStub::Handle_v_ContQuery }, + { .MainCmd = 'v', .SubStr = "Cont" , .Handler = GdbStub::Handle_v_Cont }, + + { .MainCmd = 'v', .SubStr = NULL, .Handler = NULL } +}; + +SubcmdHandler GdbStub::Handlers_q[] = { + { .MainCmd = 'q', .SubStr = "HostInfo" , .Handler = GdbStub::Handle_q_HostInfo }, + { .MainCmd = 'q', .SubStr = "ProcessInfo", .Handler = GdbStub::Handle_q_HostInfo }, + { .MainCmd = 'q', .SubStr = "Rcmd," , .Handler = GdbStub::Handle_q_Rcmd }, + { .MainCmd = 'q', .SubStr = "Supported:" , .Handler = GdbStub::Handle_q_Supported }, + { .MainCmd = 'q', .SubStr = "CRC:" , .Handler = GdbStub::Handle_q_CRC }, + { .MainCmd = 'q', .SubStr = "C" , .Handler = GdbStub::Handle_q_C }, + { .MainCmd = 'q', .SubStr = "fThreadInfo", .Handler = GdbStub::Handle_q_fThreadInfo }, + { .MainCmd = 'q', .SubStr = "sThreadInfo", .Handler = GdbStub::Handle_q_sThreadInfo }, + { .MainCmd = 'q', .SubStr = "Attached" , .Handler = GdbStub::Handle_q_Attached }, + { .MainCmd = 'q', .SubStr = "Xfer:features:read:target.xml:", .Handler = GdbStub::Handle_q_features }, + + { .MainCmd = 'q', .SubStr = NULL, .Handler = NULL }, +}; + +SubcmdHandler GdbStub::Handlers_Q[] = { + { .MainCmd = 'Q', .SubStr = "StartNoAckMode", .Handler = GdbStub::Handle_Q_StartNoAckMode }, + + { .MainCmd = 'Q', .SubStr = NULL, .Handler = NULL }, +}; + +ExecResult GdbStub::Handle_q(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return stub->SubcmdExec(cmd, len, Handlers_q); +} + +ExecResult GdbStub::Handle_v(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return stub->SubcmdExec(cmd, len, Handlers_v); +} + +ExecResult GdbStub::Handle_Q(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return stub->SubcmdExec(cmd, len, Handlers_Q); +} + +CmdHandler GdbStub::Handlers_top[] = { + { .Cmd = 'g', .Handler = GdbStub::Handle_g }, + { .Cmd = 'G', .Handler = GdbStub::Handle_G }, + { .Cmd = 'm', .Handler = GdbStub::Handle_m }, + { .Cmd = 'M', .Handler = GdbStub::Handle_M }, + { .Cmd = 'X', .Handler = GdbStub::Handle_X }, + { .Cmd = 'c', .Handler = GdbStub::Handle_c }, + { .Cmd = 's', .Handler = GdbStub::Handle_s }, + { .Cmd = 'p', .Handler = GdbStub::Handle_p }, + { .Cmd = 'P', .Handler = GdbStub::Handle_P }, + { .Cmd = 'H', .Handler = GdbStub::Handle_H }, + { .Cmd = 'T', .Handler = GdbStub::Handle_H }, + + { .Cmd = '?', .Handler = GdbStub::Handle_Question }, + { .Cmd = '!', .Handler = GdbStub::Handle_Exclamation }, + { .Cmd = 'D', .Handler = GdbStub::Handle_D }, + { .Cmd = 'r', .Handler = GdbStub::Handle_r }, + { .Cmd = 'R', .Handler = GdbStub::Handle_R }, + { .Cmd = 'k', .Handler = GdbStub::Handle_k }, + + { .Cmd = 'z', .Handler = GdbStub::Handle_z }, + { .Cmd = 'Z', .Handler = GdbStub::Handle_Z }, + + { .Cmd = 'q', .Handler = GdbStub::Handle_q }, + { .Cmd = 'v', .Handler = GdbStub::Handle_v }, + { .Cmd = 'Q', .Handler = GdbStub::Handle_Q }, + + { .Cmd = 0, .Handler = NULL } +}; + + +StubState GdbStub::HandlePacket() +{ + ExecResult r = CmdExec(Handlers_top); + + if (r == ExecResult::MustBreak) + { + if (Stat == TgtStatus::None || Stat == TgtStatus::Running) + Stat = TgtStatus::BreakReq; + return StubState::Break; + } + else if (r == ExecResult::InitialBreak) + { + Stat = TgtStatus::BreakReq; + return StubState::Attach; + /*} + else if (r == ExecResult::Detached) + { + Stat = TgtStatus::None; + return StubState::Disconnect;*/ + } + else if (r == ExecResult::Continue) + { + Stat = TgtStatus::Running; + return StubState::Continue; + } + else if (r == ExecResult::Step) + { + return StubState::Step; + } + else if (r == ExecResult::Ok || r == ExecResult::UnkCmd) + { + return StubState::None; + } + else + { + Stat = TgtStatus::None; + return StubState::Disconnect; + } +} + +StubState GdbStub::Poll(bool wait) +{ + int r; + + if (ConnFd <= 0) + { + SocketSetBlocking(SockFd, wait); + + // not yet connected, so let's wait for one + // nonblocking only done in part of read_packet(), so that it can still + // quickly handle partly-received packets + struct sockaddr_in* client = (struct sockaddr_in*)ClientSA; + socklen_t len = sizeof(*client); +#if MOCKTEST + ConnFd = 0; +#else +#ifdef __linux__ + ConnFd = accept4(SockFd, (struct sockaddr*)client, &len, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC); +#else + ConnFd = accept(SockFd, (struct sockaddr*)client, &len); +#endif +#endif + + if (ConnFd < 0) return StubState::NoConn; + + u8 a; + if (Proto::WaitAckBlocking(ConnFd, &a, 1000) < 0) + { + Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n"); + close(ConnFd); + ConnFd = 0; + return StubState::Disconnect; + } + + if (a != '+') + { + Log(LogLevel::Error, "[GDB] inital handshake: unexpected character '%c'!\n", a); + } + SendAck(); + + Stat = TgtStatus::Running; // on connected + StatFlag = false; + } + + if (StatFlag) + { + StatFlag = false; + //Log(LogLevel::Debug, "[GDB] STAT FLAG WAS TRUE\n"); + + Handle_Question(this, NULL, 0); // ugly hack but it should work + } + +#if MOCKTEST + // nothing... +#else +#ifndef _WIN32 + struct pollfd pfd; + pfd.fd = ConnFd; + pfd.events = POLLIN; + pfd.revents = 0; + + r = poll(&pfd, 1, wait ? -1 : 0); + + if (r == 0) return StubState::None; // nothing is happening + + if (pfd.revents & (POLLHUP|POLLERR|POLLNVAL)) + { + // oopsie, something happened + Disconnect(); + return StubState::Disconnect; + } +#else + fd_set infd, outfd, errfd; + FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); + FD_SET(ConnFd, &infd); + + struct timeval to; + if (wait) + { + to.tv_sec = ~(time_t)0; + to.tv_usec = ~(long)0; + } + else + { + to.tv_sec = 0; + to.tv_usec = 0; + } + + r = select(ConnFd+1, &infd, &outfd, &errfd, &to); + + if (FD_ISSET(ConnFd, &errfd)) + { + Disconnect(); + return StubState::Disconnect; + } + else if (!FD_ISSET(ConnFd, &infd)) + { + return StubState::None; + } +#endif +#endif + + ReadResult res = Proto::MsgRecv(ConnFd, Cmdbuf); + + switch (res) + { + case ReadResult::NoPacket: + return StubState::None; + case ReadResult::Break: + return StubState::Break; + case ReadResult::Wut: + Log(LogLevel::Info, "[GDB] WUT\n"); + case_gdbp_eof: + case ReadResult::Eof: + Log(LogLevel::Info, "[GDB] EOF!\n"); + close(ConnFd); + ConnFd = 0; + return StubState::Disconnect; + case ReadResult::CksumErr: + Log(LogLevel::Info, "[GDB] checksum err!\n"); + if (SendNak() < 0) { + Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n"); + goto case_gdbp_eof; + } + return StubState::None; + case ReadResult::CmdRecvd: + /*if (SendAck() < 0) { + Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); + goto case_gdbp_eof; + }*/ + break; + } + + return HandlePacket(); +} + +ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* handlers) +{ + //Log(LogLevel::Debug, "[GDB] subcommand in: '%s'\n", cmd); + + for (size_t i = 0; handlers[i].Handler != NULL; ++i) { + // check if prefix matches + if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr))) + { + if (SendAck() < 0) + { + Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); + return ExecResult::NetErr; + } + return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr)); + } + } + + Log(LogLevel::Info, "[GDB] unknown subcommand '%s'!\n", cmd); + /*if (SendNak() < 0) + { + Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n"); + return ExecResult::NetErr; + }*/ + //Resp("E99"); + Resp(NULL, 0); + return ExecResult::UnkCmd; +} + +ExecResult GdbStub::CmdExec(const CmdHandler* handlers) +{ + Log(LogLevel::Debug, "[GDB] command in: '%s'\n", Cmdbuf); + + for (size_t i = 0; handlers[i].Handler != NULL; ++i) + { + if (handlers[i].Cmd == Cmdbuf[0]) + { + if (SendAck() < 0) + { + Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); + return ExecResult::NetErr; + } + return handlers[i].Handler(this, &Cmdbuf[1], Cmdlen-1); + } + } + + Log(LogLevel::Info, "[GDB] unknown command '%c'!\n", Cmdbuf[0]); + /*if (SendNak() < 0) + { + Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n"); + return ExecResult::NetErr; + }*/ + //RespStr("E99"); + Resp(NULL, 0); + return ExecResult::UnkCmd; +} + + +void GdbStub::SignalStatus(TgtStatus stat, u32 arg) +{ + //Log(LogLevel::Debug, "[GDB] SIGNAL STATUS %d!\n", stat); + + this->Stat = stat; + StatFlag = true; + + if (stat == TgtStatus::Bkpt) CurBkpt = arg; + else if (stat == TgtStatus::Watchpt) CurWatchpt = arg; +} + + +StubState GdbStub::Enter(bool stay, TgtStatus stat, u32 arg, bool wait_for_conn) +{ + if (stat != TgtStatus::NoEvent) SignalStatus(stat, arg); + + StubState st; + bool do_next = true; + do + { + bool was_conn = ConnFd > 0; + st = Poll(wait_for_conn); + bool has_conn = ConnFd > 0; + + if (has_conn && !was_conn) stay = true; + + switch (st) + { + case StubState::Break: + Log(LogLevel::Info, "[GDB] break execution\n"); + SignalStatus(TgtStatus::BreakReq, ~(u32)0); + break; + case StubState::Continue: + Log(LogLevel::Info, "[GDB] continue execution\n"); + do_next = false; + break; + case StubState::Step: + Log(LogLevel::Info, "[GDB] single-step\n"); + do_next = false; + break; + case StubState::Disconnect: + Log(LogLevel::Info, "[GDB] disconnect\n"); + SignalStatus(TgtStatus::None, ~(u32)0); + do_next = false; + break; + default: break; + } + } + while (do_next && stay); + + if (st != StubState::None && st != StubState::NoConn) + { + Log(LogLevel::Debug, "[GDB] enter exit: %d\n", st); + } + return st; +} + +void GdbStub::AddBkpt(u32 addr, int kind) +{ + BpWp np; + np.addr = addr ^ (addr & 1); // clear lowest bit to not break on thumb mode weirdnesses + np.len = 0; + np.kind = kind; + + { + // already in the map + auto search = BpList.find(np.addr); + if (search != BpList.end()) return; + } + + BpList.insert({np.addr, np}); + + Log(LogLevel::Debug, "[GDB] added bkpt:\n"); + size_t i = 0; + for (auto search = BpList.begin(); search != BpList.end(); ++search, ++i) + { + Log(LogLevel::Debug, "\t[%zu]: addr=%08x, kind=%d\n", i, search->first, search->second.kind); + } +} +void GdbStub::AddWatchpt(u32 addr, u32 len, int kind) +{ + BpWp np; + np.addr = addr; + np.len = len; + np.kind = kind; + + for (auto search = WpList.begin(); search != WpList.end(); ++search) + { + if (search->addr > addr) + { + WpList.insert(search, np); + return; + } + else if (search->addr == addr && search->kind == kind) + { + if (search->len < len) search->len = len; + return; + } + } + + WpList.push_back(np); +} + +void GdbStub::DelBkpt(u32 addr, int kind) +{ + addr = addr ^ (addr & 1); + + auto search = BpList.find(addr); + if (search != BpList.end()) + { + BpList.erase(search); + } +} +void GdbStub::DelWatchpt(u32 addr, u32 len, int kind) +{ + (void)len; (void)kind; + + for (auto search = WpList.begin(); search != WpList.end(); ++search) + { + if (search->addr == addr && search->kind == kind) + { + WpList.erase(search); + return; + } + else if (search->addr > addr) return; + } +} + +void GdbStub::DelAllBpWp() +{ + BpList.erase(BpList.begin(), BpList.end()); + WpList.erase(WpList.begin(), WpList.end()); +} + +StubState GdbStub::CheckBkpt(u32 addr, bool enter, bool stay) +{ + addr ^= (addr & 1); // clear lowest bit to not break on thumb mode weirdnesses + + auto search = BpList.find(addr); + if (search == BpList.end()) return StubState::CheckNoHit; + + if (enter) + { + StubState r = Enter(stay, TgtStatus::Bkpt, addr); + Log(LogLevel::Debug, "[GDB] ENTER st=%d\n", r); + return r; + } + else + { + SignalStatus(TgtStatus::Bkpt, addr); + return StubState::None; + } +} +StubState GdbStub::CheckWatchpt(u32 addr, int kind, bool enter, bool stay) +{ + for (auto search = WpList.begin(); search != WpList.end(); ++search) + { + if (search->addr > addr) break; + + if (addr >= search->addr && addr < search->addr + search->len && search->kind == kind) + { + if (enter) return Enter(stay, TgtStatus::Watchpt, addr); + else + { + SignalStatus(TgtStatus::Watchpt, addr); + return StubState::None; + } + } + } + + return StubState::CheckNoHit; +} + +int GdbStub::SendAck() +{ + if (NoAck) return 1; + return Proto::SendAck(ConnFd); +} +int GdbStub::SendNak() +{ + if (NoAck) return 1; + return Proto::SendNak(ConnFd); +} + +int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2) +{ + return Proto::Resp(ConnFd, data1, len1, data2, len2, NoAck); +} +int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2) +{ + return Proto::Resp(ConnFd, (const u8*)data1, len1, data2, len2, NoAck); +} +#if defined(__GCC__) || defined(__clang__) +__attribute__((__format__(printf, 2/*includes implicit this*/, 3))) +#endif +int GdbStub::RespFmt(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int r = vsnprintf((char*)&Proto::RespBuf[1], sizeof(Proto::RespBuf)-5, fmt, args); + va_end(args); + + if (r < 0) return r; + + if ((size_t)r >= sizeof(Proto::RespBuf)-5) + { + Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n", + (ssize_t)r - (ssize_t)(sizeof(Proto::RespBuf)-5)); + r = sizeof(Proto::RespBuf)-5; + } + + return Resp(&Proto::RespBuf[1], r); +} + +int GdbStub::RespStr(const char* str) +{ + return Resp((const u8*)str, strlen(str)); +} + +} + diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h new file mode 100644 index 00000000..99b88158 --- /dev/null +++ b/src/debug/GdbStub.h @@ -0,0 +1,186 @@ + +#ifndef GDBSTUB_H_ +#define GDBSTUB_H_ + +#include +#include +#include +#include + +#include "../types.h" + +#include "GdbArch.h" + +namespace Gdb +{ + +using namespace melonDS; +enum class TgtStatus +{ + NoEvent, + + None, + Running, + SingleStep, + BreakReq, // "break" command from gdb client + Bkpt, + Watchpt, + BkptInsn, // "bkpt" instruction + FaultData, // data abort + FaultIAcc, // instruction fetch abort + FaultInsn, // illegal instruction +}; + +class StubCallbacks +{ +public: + StubCallbacks(){} + virtual ~StubCallbacks(){}; + + virtual int GetCPU() const = 0; // 7 or 9 (currently, maybe also xtensa in the future?) + + // 0..14: as usual + // 15: pc *pipeline-corrected* + // 16: cpsr + virtual u32 ReadReg (Register reg) = 0; + virtual void WriteReg(Register reg, u32 value) = 0; + + virtual u32 ReadMem (u32 addr, int len) = 0; + virtual void WriteMem(u32 addr, int len, u32 value) = 0; + + virtual void ResetGdb() = 0; + virtual int RemoteCmd(const u8* cmd, size_t len) = 0; +}; + +enum class StubState +{ + NoConn, + None, + Break, + Continue, + Step, + Disconnect, + Attach, + CheckNoHit +}; + +enum class ReadResult +{ + NoPacket, + Eof, + CksumErr, + CmdRecvd, + Wut, + Break +}; + +enum class ExecResult +{ + Ok, + UnkCmd, + NetErr, + InitialBreak, + MustBreak, + Detached, + Step, + Continue +}; + +class GdbStub; + +typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len); + +struct SubcmdHandler +{ + char MainCmd; + const char* SubStr; + GdbProtoCmd Handler; +}; + +struct CmdHandler +{ + char Cmd; + GdbProtoCmd Handler; +}; + +class GdbStub +{ +public: + struct BpWp + { + public: + u32 addr, len; + int kind; + }; + + GdbStub(StubCallbacks* cb, int port); + ~GdbStub(); + + bool Init(); + void Close(); + + StubState Poll(bool wait = false); + void SignalStatus(TgtStatus stat, u32 arg); + StubState Enter(bool stay, TgtStatus stat=TgtStatus::NoEvent, u32 arg=~(u32)0u, bool wait_for_conn=false); + + // kind: 2=thumb, 3=thumb2 (not relevant), 4=arm + void AddBkpt(u32 addr, int kind); + void DelBkpt(u32 addr, int kind); + // kind: 2=read, 3=write, 4=rdwr + void AddWatchpt(u32 addr, u32 len, int kind); + void DelWatchpt(u32 addr, u32 len, int kind); + + void DelAllBpWp(); + + StubState CheckBkpt(u32 addr, bool enter, bool stay); + StubState CheckWatchpt(u32 addr, int kind, bool enter, bool stay); + +#include "GdbCmds.h" + + Gdb::ExecResult SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* handlers); + Gdb::ExecResult CmdExec(const CmdHandler* handlers); + +public: + int SendAck(); + int SendNak(); + + int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0); + int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0); +#if defined(__GCC__) || defined(__clang__) + __attribute__((__format__(printf, 2, 3))) +#endif + int RespFmt(const char* fmt, ...); + + int RespStr(const char* str); + +private: + void Disconnect(); + StubState HandlePacket(); + +private: + StubCallbacks* Cb; + + //struct sockaddr_in server, client; + void *ServerSA, *ClientSA; + int Port; + int SockFd; + int ConnFd; + + TgtStatus Stat; + u32 CurBkpt, CurWatchpt; + bool StatFlag; + bool NoAck; + + std::map BpList; + std::vector WpList; + + static SubcmdHandler Handlers_v[]; + static SubcmdHandler Handlers_q[]; + static SubcmdHandler Handlers_Q[]; + static CmdHandler Handlers_top[]; +}; + +} + +#endif + diff --git a/src/debug/gdb_test/.gitignore b/src/debug/gdb_test/.gitignore new file mode 100644 index 00000000..218500b3 --- /dev/null +++ b/src/debug/gdb_test/.gitignore @@ -0,0 +1,2 @@ +obj/ +test-gdb diff --git a/src/debug/gdb_test/Makefile b/src/debug/gdb_test/Makefile new file mode 100644 index 00000000..e8357955 --- /dev/null +++ b/src/debug/gdb_test/Makefile @@ -0,0 +1,28 @@ + +default: all + +all: test-gdb + +CPPFLAGS += -Werror=implicit-function-declaration -Werror=int-conversion \ + -Werror=return-type -Werror=uninitialized \ + -I../ -I../../ -Og -g -Wall \ + -Wno-switch -Wno-pointer-sign + +obj/: + @mkdir -vp "$@" + +test-gdb: obj/GdbProto.o obj/GdbStub.o obj/GdbCmds.o obj/main.o obj/CRC32.o + $(CXX) $(CPPFLAGS) $(LDFLAGS) -o "$@" $^ + +obj/Gdb%.o: ../Gdb%.cpp obj/ + $(CXX) $(CPPFLAGS) -c -o "$@" "$<" + +obj/main.o: main.cpp obj/ + $(CXX) $(CPPFLAGS) -c -o "$@" "$<" + +obj/CRC32.o: ../../CRC32.cpp obj/ + $(CXX) $(CPPFLAGS) -c -o "$@" "$<" + +clean: + @$(RM) -rv obj/ test-gdb + diff --git a/src/debug/gdb_test/main.cpp b/src/debug/gdb_test/main.cpp new file mode 100644 index 00000000..afdfa2cd --- /dev/null +++ b/src/debug/gdb_test/main.cpp @@ -0,0 +1,124 @@ + +#include +#include +#include +#include + +#include "GdbStub.h" +#include "Platform.h" + +class Debug : public Gdb::StubCallbacks +{ +public: + Debug(){} + ~Debug(){} + + int GetCPU() const override { return 9; } + + u32 ReadReg(Gdb::Register reg) override + { + printf("[==>] read reg %d\n", (int)reg); + if (reg == Gdb::Register::pc) return 0x000000df; // cpsr: irq,fiq disabled, arm, sys mode + else return 0x69420; + } + void WriteReg(Gdb::Register reg, u32 value) override + { + printf("[==>] write reg %d: 0x%08x\n", (int)reg, value); + } + + u32 ReadMem(u32 addr, int len) override + { + static const u32 words[] = { + 0xeafffffe, + 0xe0211002, + 0xe12fff1e, + 0 + }; + + printf("[==>] read mem 0x%08x (size %u)\n", addr, len); + + // $: b $ (arm) + return words[(addr>>2)&3] & ((1uLL<] write addr 0x%08x (size %u): 0x%08x\n", addr, len, value); + } + + void ResetGdb() override + { + printf("[==>] RESET!!!\n"); + } + int RemoteCmd(const u8* cmd, size_t len) override + { + printf("[==>] Rcmd: %s\n", cmd); + return 0; + } +}; + +int main(int argc, char** argv) { + Debug debug; + + Gdb::GdbStub stub(&debug, (argc > 1) ? atoi(argv[1]) : 3333); + if (!stub.Init()) return 1; + + do + { + while (true) + { + Gdb::StubState s = stub.Poll(); + + if (s == Gdb::StubState::None || s == Gdb::StubState::NoConn) + { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000*1000; // 1 ms + nanosleep(&ts, NULL); + continue; + } + + switch (s) + { + case Gdb::StubState::Attach: + printf("[==>] attached\n"); + break; + case Gdb::StubState::Break: + printf("[==>] break execution\n"); + stub.SignalStatus(Gdb::TgtStatus::BreakReq, ~(u32)0); + break; + case Gdb::StubState::Continue: + printf("[==>] continue execution\n"); + // TODO: send signal status on SIGSTOP? eh. + break; + case Gdb::StubState::Step: + printf("[==>] single-step\n"); + stub.SignalStatus(Gdb::TgtStatus::SingleStep, ~(u32)0); + break; + case Gdb::StubState::Disconnect: + printf("[==>] disconnect\n"); + stub.SignalStatus(Gdb::TgtStatus::None, ~(u32)0); + break; + } + + if (s == Gdb::StubState::Disconnect) break; + } + } + while (false); + + stub.Close(); + return 0; +} + +namespace Platform +{ +void Log(LogLevel level, const char* fmt, ...) +{ + if (fmt == nullptr) return; + + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} +} + diff --git a/src/debug/hexutil.h b/src/debug/hexutil.h new file mode 100644 index 00000000..63d1deb7 --- /dev/null +++ b/src/debug/hexutil.h @@ -0,0 +1,78 @@ + +#ifndef HEXUTIL_GDB_H_ +#define HEXUTIL_GDB_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +namespace melonDS +{ +inline static uint8_t hex2nyb(uint8_t v) +{ + if (v >= '0' && v <= '9') return v - '0'; + else if (v >= 'A' && v <= 'F') return v - 'A' + 0xa; + else if (v >= 'a' && v <= 'f') return v - 'a' + 0xa; + else + { + __builtin_trap(); + return 0xcc; + } +} +inline static uint8_t nyb2hex(uint8_t v) +{ + v &= 0xf; + if (v >= 0xa) return v - 0xa + 'a'; + else return v - 0x0 + '0'; +} + +inline static void hexfmt8(uint8_t* dst, uint8_t v) +{ + dst[0] = nyb2hex(v>>4); + dst[1] = nyb2hex(v>>0); +} +inline static uint8_t unhex8(const uint8_t* src) +{ + return (hex2nyb(src[0]) << 4) | hex2nyb(src[1]); +} + +inline static void hexfmt16(uint8_t* dst, uint16_t v) +{ + dst[0] = nyb2hex(v>> 4); + dst[1] = nyb2hex(v>> 0); + dst[2] = nyb2hex(v>>12); + dst[3] = nyb2hex(v>> 8); +} +inline static uint16_t unhex16(const uint8_t* src) +{ + return unhex8(&src[0*2]) | ((uint16_t)unhex8(&src[1*2]) << 8); +} + +inline static void hexfmt32(uint8_t* dst, uint32_t v) +{ + for (size_t i = 0; i < 4; ++i, v >>= 8) + { + dst[2*i+0] = nyb2hex(v>>4); + dst[2*i+1] = nyb2hex(v>>0); + } +} +inline static uint32_t unhex32(const uint8_t* src) +{ + uint32_t v = 0; + for (size_t i = 0; i < 4; ++i) + { + v |= (uint32_t)unhex8(&src[i*2]) << (i*8); + } + return v; +} + +#ifdef __cplusplus +} +#endif + +} +#endif + diff --git a/src/dolphin/Arm64Emitter.h b/src/dolphin/Arm64Emitter.h index 0b066ded..0fe4c919 100644 --- a/src/dolphin/Arm64Emitter.h +++ b/src/dolphin/Arm64Emitter.h @@ -14,6 +14,8 @@ namespace Arm64Gen { +using namespace melonDS; +using namespace Common; // X30 serves a dual purpose as a link register // Encoded as // Types: diff --git a/src/dolphin/ArmCommon.h b/src/dolphin/ArmCommon.h index 6d82e9d7..cae2ecb9 100644 --- a/src/dolphin/ArmCommon.h +++ b/src/dolphin/ArmCommon.h @@ -24,4 +24,4 @@ enum CCFlags CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same CC_LO = CC_CC, // Alias of CC_CC Unsigned lower }; -const u32 NO_COND = 0xE0000000; +const melonDS::u32 NO_COND = 0xE0000000; diff --git a/src/dolphin/BitSet.h b/src/dolphin/BitSet.h index 09cc1ce6..424364eb 100644 --- a/src/dolphin/BitSet.h +++ b/src/dolphin/BitSet.h @@ -10,6 +10,7 @@ namespace Common { +using namespace melonDS; #if defined(__GNUC__) || defined(__clang__) __attribute((always_inline)) static constexpr int CountSetBits(u8 val) { @@ -218,9 +219,10 @@ public: constexpr Iterator end() const { return Iterator(m_val, -1); } IntTy m_val; }; + +using BitSet8 = BitSet; +using BitSet16 = BitSet; +using BitSet32 = BitSet; +using BitSet64 = BitSet; } // namespace Common -using BitSet8 = Common::BitSet; -using BitSet16 = Common::BitSet; -using BitSet32 = Common::BitSet; -using BitSet64 = Common::BitSet; diff --git a/src/dolphin/MathUtil.h b/src/dolphin/MathUtil.h index b1dbbaec..800a3275 100644 --- a/src/dolphin/MathUtil.h +++ b/src/dolphin/MathUtil.h @@ -38,7 +38,7 @@ constexpr bool IsPow2(T imm) return imm > 0 && (imm & (imm - 1)) == 0; } -constexpr u32 NextPowerOf2(u32 value) +constexpr melonDS::u32 NextPowerOf2(melonDS::u32 value) { --value; value |= value >> 1; @@ -99,7 +99,7 @@ struct Rectangle float MathFloatVectorSum(const std::vector&); // Rounds down. 0 -> undefined -inline int IntLog2(u64 val) +inline int IntLog2(melonDS::u64 val) { #if defined(__GNUC__) return 63 - __builtin_clzll(val); diff --git a/src/dolphin/x64ABI.cpp b/src/dolphin/x64ABI.cpp index d86a1589..d85168a1 100644 --- a/src/dolphin/x64ABI.cpp +++ b/src/dolphin/x64ABI.cpp @@ -6,8 +6,9 @@ #include "x64ABI.h" #include "x64Emitter.h" -using namespace Gen; - +namespace Gen +{ +using namespace Common; // Shared code between Win64 and Unix64 void XEmitter::ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size, @@ -117,3 +118,4 @@ void XEmitter::MOVTwo(int bits, Gen::X64Reg dst1, Gen::X64Reg src1, s32 offset1, ADD(bits, R(dst1), Imm32(offset1)); } } +} \ No newline at end of file diff --git a/src/dolphin/x64CPUDetect.cpp b/src/dolphin/x64CPUDetect.cpp index 49b51c9d..23390486 100644 --- a/src/dolphin/x64CPUDetect.cpp +++ b/src/dolphin/x64CPUDetect.cpp @@ -17,6 +17,8 @@ #include #endif +using namespace melonDS; + static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { #ifdef __FreeBSD__ diff --git a/src/dolphin/x64Emitter.cpp b/src/dolphin/x64Emitter.cpp index fd90ba7f..a4543afd 100644 --- a/src/dolphin/x64Emitter.cpp +++ b/src/dolphin/x64Emitter.cpp @@ -12,6 +12,8 @@ #include "Compat.h" #include "CommonFuncs.h" +using namespace melonDS; + namespace Gen { // TODO(ector): Add EAX special casing, for ever so slightly smaller code. diff --git a/src/dolphin/x64Emitter.h b/src/dolphin/x64Emitter.h index 87996003..36603218 100644 --- a/src/dolphin/x64Emitter.h +++ b/src/dolphin/x64Emitter.h @@ -19,6 +19,8 @@ namespace Gen { +using namespace melonDS; +using namespace Common; enum CCFlags { CC_O = 0, diff --git a/src/fatfs/ff.c b/src/fatfs/ff.c index 9d212949..385da84e 100644 --- a/src/fatfs/ff.c +++ b/src/fatfs/ff.c @@ -728,13 +728,13 @@ static int dbc_2nd (BYTE c) #if FF_USE_LFN -/* Get a Unicode code point from the TCHAR string in defined API encodeing */ +/* Get a Unicode code point from the FF_TCHAR string in defined API encodeing */ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */ - const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */ + const FF_TCHAR** str /* Pointer to pointer to FF_TCHAR string in configured encoding */ ) { DWORD uc; - const TCHAR *p = *str; + const FF_TCHAR *p = *str; #if FF_LFN_UNICODE == 1 /* UTF-16 input */ WCHAR wc; @@ -771,7 +771,7 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on } #elif FF_LFN_UNICODE == 3 /* UTF-32 input */ - uc = (TCHAR)*p++; /* Get a unit */ + uc = (FF_TCHAR)*p++; /* Get a unit */ if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* Wrong code? */ if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ @@ -800,7 +800,7 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on /* Store a Unicode char in defined API encoding */ static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */ - TCHAR* buf, /* Output buffer */ + FF_TCHAR* buf, /* Output buffer */ UINT szb /* Size of the buffer */ ) { @@ -824,20 +824,20 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over if (chr < 0x80) { /* Single byte code? */ if (szb < 1) return 0; /* Buffer overflow? */ - *buf = (TCHAR)chr; + *buf = (FF_TCHAR)chr; return 1; } if (chr < 0x800) { /* 2-byte sequence? */ if (szb < 2) return 0; /* Buffer overflow? */ - *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); - *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + *buf++ = (FF_TCHAR)(0xC0 | (chr >> 6 & 0x1F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F)); return 2; } if (chr < 0x10000) { /* 3-byte sequence? */ if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */ - *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); - *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); - *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + *buf++ = (FF_TCHAR)(0xE0 | (chr >> 12 & 0x0F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F)); return 3; } /* 4-byte sequence */ @@ -846,10 +846,10 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ chr = (hc | chr) + 0x10000; - *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); - *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); - *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); - *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + *buf++ = (FF_TCHAR)(0xF0 | (chr >> 18 & 0x07)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 12 & 0x3F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F)); return 4; #elif FF_LFN_UNICODE == 3 /* UTF-32 output */ @@ -862,7 +862,7 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ chr = (hc | chr) + 0x10000; } - *buf++ = (TCHAR)chr; + *buf++ = (FF_TCHAR)chr; return 1; #else /* ANSI/OEM output */ @@ -872,11 +872,11 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over if (wc >= 0x100) { /* Is this a DBC? */ if (szb < 2) return 0; *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ - *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ + *buf++ = (FF_TCHAR)wc; /* Store DBC 2nd byte */ return 2; } if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */ - *buf++ = (TCHAR)wc; /* Store the character */ + *buf++ = (FF_TCHAR)wc; /* Store the character */ return 1; #endif } @@ -2595,7 +2595,7 @@ static void get_fileinfo ( FATFS *fs = dp->obj.fs; UINT nw; #else - TCHAR c; + FF_TCHAR c; #endif @@ -2668,7 +2668,7 @@ static void get_fileinfo ( if (nw == 0) { di = 0; break; } /* Buffer overflow? */ di += nw; #else /* ANSI/OEM output */ - fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ + fno->altname[di++] = (FF_TCHAR)wc; /* Store it without any conversion */ #endif } fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ @@ -2681,7 +2681,7 @@ static void get_fileinfo ( wc = (WCHAR)fno->altname[si]; if (wc == '.') lcf = NS_EXT; if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20; - fno->fname[di] = (TCHAR)wc; + fno->fname[di] = (FF_TCHAR)wc; } } fno->fname[di] = 0; /* Terminate the LFN */ @@ -2691,7 +2691,7 @@ static void get_fileinfo ( #else /* Non-LFN configuration */ si = di = 0; while (si < 11) { /* Copy name body and extension */ - c = (TCHAR)dp->dir[si++]; + c = (FF_TCHAR)dp->dir[si++]; if (c == ' ') continue; /* Skip padding spaces */ if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */ if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ @@ -2719,7 +2719,7 @@ static void get_fileinfo ( static DWORD get_achar ( /* Get a character and advance ptr */ - const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ + const FF_TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ ) { DWORD chr; @@ -2750,13 +2750,13 @@ static DWORD get_achar ( /* Get a character and advance ptr */ static int pattern_match ( /* 0:mismatched, 1:matched */ - const TCHAR* pat, /* Matching pattern */ - const TCHAR* nam, /* String to be tested */ + const FF_TCHAR* pat, /* Matching pattern */ + const FF_TCHAR* nam, /* String to be tested */ UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */ UINT recur /* Recursion count */ ) { - const TCHAR *pptr, *nptr; + const FF_TCHAR *pptr, *nptr; DWORD pchr, nchr; UINT sk; @@ -2800,7 +2800,7 @@ static int pattern_match ( /* 0:mismatched, 1:matched */ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ FF_DIR* dp, /* Pointer to the directory object */ - const TCHAR** path /* Pointer to pointer to the segment in the path string */ + const FF_TCHAR** path /* Pointer to pointer to the segment in the path string */ ) { #if FF_USE_LFN /* LFN configuration */ @@ -2808,7 +2808,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr WCHAR wc, *lfn; DWORD uc; UINT i, ni, si, di; - const TCHAR *p; + const FF_TCHAR *p; /* Create LFN into LFN working buffer */ @@ -3002,7 +3002,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ FF_DIR* dp, /* Directory object to return last directory and found object */ - const TCHAR* path /* Full-path string to find a file or directory */ + const FF_TCHAR* path /* Full-path string to find a file or directory */ ) { FRESULT res; @@ -3088,11 +3088,11 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ /*-----------------------------------------------------------------------*/ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */ - const TCHAR** path /* Pointer to pointer to the path name */ + const FF_TCHAR** path /* Pointer to pointer to the path name */ ) { - const TCHAR *tp, *tt; - TCHAR tc; + const FF_TCHAR *tp, *tt; + FF_TCHAR tc; int i; int vol = -1; #if FF_STR_VOLUME_ID /* Find string volume ID */ @@ -3118,7 +3118,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb c = *sp++; tc = *tp++; if (IsLower(c)) c -= 0x20; if (IsLower(tc)) tc -= 0x20; - } while (c && (TCHAR)c == tc); + } while (c && (FF_TCHAR)c == tc); } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */ } #endif @@ -3138,7 +3138,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb c = *sp++; tc = *(++tt); if (IsLower(c)) c -= 0x20; if (IsLower(tc)) tc -= 0x20; - } while (c && (TCHAR)c == tc); + } while (c && (FF_TCHAR)c == tc); } while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ vol = i; /* Drive number */ @@ -3330,7 +3330,7 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ /*-----------------------------------------------------------------------*/ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ - const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + const FF_TCHAR** path, /* Pointer to pointer to the path name (drive number) */ FATFS** rfs, /* Pointer to pointer to the found filesystem object */ BYTE mode /* !=0: Check write protection for write access */ ) @@ -3604,14 +3604,14 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ FRESULT f_mount ( FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/ - const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + const FF_TCHAR* path, /* Logical drive number to be mounted/unmounted */ BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */ ) { FATFS *cfs; int vol; FRESULT res; - const TCHAR *rp = path; + const FF_TCHAR *rp = path; /* Get logical drive number */ @@ -3652,7 +3652,7 @@ FRESULT f_mount ( FRESULT f_open ( FF_FIL* fp, /* Pointer to the blank file object */ - const TCHAR* path, /* Pointer to the file name */ + const FF_TCHAR* path, /* Pointer to the file name */ BYTE mode /* Access mode and open mode flags */ ) { @@ -4186,7 +4186,7 @@ FRESULT f_close ( /*-----------------------------------------------------------------------*/ FRESULT f_chdrive ( - const TCHAR* path /* Drive number to set */ + const FF_TCHAR* path /* Drive number to set */ ) { int vol; @@ -4203,7 +4203,7 @@ FRESULT f_chdrive ( FRESULT f_chdir ( - const TCHAR* path /* Pointer to the directory path */ + const FF_TCHAR* path /* Pointer to the directory path */ ) { #if FF_STR_VOLUME_ID == 2 @@ -4265,8 +4265,8 @@ FRESULT f_chdir ( #if FF_FS_RPATH >= 2 FRESULT f_getcwd ( - TCHAR* buff, /* Pointer to the directory path */ - UINT len /* Size of buff in unit of TCHAR */ + FF_TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of buff in unit of FF_TCHAR */ ) { FRESULT res; @@ -4274,7 +4274,7 @@ FRESULT f_getcwd ( FATFS *fs; UINT i, n; DWORD ccl; - TCHAR *tp = buff; + FF_TCHAR *tp = buff; #if FF_VOLUMES >= 2 UINT vl; #if FF_STR_VOLUME_ID @@ -4287,7 +4287,7 @@ FRESULT f_getcwd ( /* Get logical drive */ buff[0] = 0; /* Set null string to get current volume */ - res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + res = mount_volume((const FF_TCHAR**)&buff, &fs, 0); /* Get current volume */ if (res == FR_OK) { dj.obj.fs = fs; INIT_NAMBUF(fs); @@ -4328,15 +4328,15 @@ FRESULT f_getcwd ( #if FF_STR_VOLUME_ID >= 1 /* String volume ID */ for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ; if (i >= n + 2) { - if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; - for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; - if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; + if (FF_STR_VOLUME_ID == 2) *tp++ = (FF_TCHAR)'/'; + for (vl = 0; vl < n; *tp++ = (FF_TCHAR)vp[vl], vl++) ; + if (FF_STR_VOLUME_ID == 1) *tp++ = (FF_TCHAR)':'; vl++; } #else /* Numeric volume ID */ if (i >= 3) { - *tp++ = (TCHAR)'0' + CurrVol; - *tp++ = (TCHAR)':'; + *tp++ = (FF_TCHAR)'0' + CurrVol; + *tp++ = (FF_TCHAR)':'; vl = 2; } #endif @@ -4530,7 +4530,7 @@ FRESULT f_lseek ( FRESULT f_opendir ( FF_DIR* dp, /* Pointer to directory object to create */ - const TCHAR* path /* Pointer to the directory path */ + const FF_TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; @@ -4688,8 +4688,8 @@ FRESULT f_findnext ( FRESULT f_findfirst ( FF_DIR* dp, /* Pointer to the blank directory object */ FF_FILINFO* fno, /* Pointer to the file information structure */ - const TCHAR* path, /* Pointer to the directory to open */ - const TCHAR* pattern /* Pointer to the matching pattern */ + const FF_TCHAR* path, /* Pointer to the directory to open */ + const FF_TCHAR* pattern /* Pointer to the matching pattern */ ) { FRESULT res; @@ -4713,7 +4713,7 @@ FRESULT f_findfirst ( /*-----------------------------------------------------------------------*/ FRESULT f_stat ( - const TCHAR* path, /* Pointer to the file path */ + const FF_TCHAR* path, /* Pointer to the file path */ FF_FILINFO* fno /* Pointer to file information to return */ ) { @@ -4748,7 +4748,7 @@ FRESULT f_stat ( /*-----------------------------------------------------------------------*/ FRESULT f_getfree ( - const TCHAR* path, /* Logical drive number */ + const FF_TCHAR* path, /* Logical drive number */ DWORD* nclst, /* Pointer to a variable to return number of free clusters */ FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */ ) @@ -4890,7 +4890,7 @@ FRESULT f_truncate ( /*-----------------------------------------------------------------------*/ FRESULT f_unlink ( - const TCHAR* path /* Pointer to the file or directory path */ + const FF_TCHAR* path /* Pointer to the file or directory path */ ) { FRESULT res; @@ -4984,7 +4984,7 @@ FRESULT f_unlink ( /*-----------------------------------------------------------------------*/ FRESULT f_mkdir ( - const TCHAR* path /* Pointer to the directory path */ + const FF_TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; @@ -5068,8 +5068,8 @@ FRESULT f_mkdir ( /*-----------------------------------------------------------------------*/ FRESULT f_rename ( - const TCHAR* path_old, /* Pointer to the object name to be renamed */ - const TCHAR* path_new /* Pointer to the new name */ + const FF_TCHAR* path_old, /* Pointer to the object name to be renamed */ + const FF_TCHAR* path_new /* Pointer to the new name */ ) { FRESULT res; @@ -5178,7 +5178,7 @@ FRESULT f_rename ( /*-----------------------------------------------------------------------*/ FRESULT f_chmod ( - const TCHAR* path, /* Pointer to the file path */ + const FF_TCHAR* path, /* Pointer to the file path */ BYTE attr, /* Attribute bits */ BYTE mask /* Attribute mask to change */ ) @@ -5225,7 +5225,7 @@ FRESULT f_chmod ( /*-----------------------------------------------------------------------*/ FRESULT f_utime ( - const TCHAR* path, /* Pointer to the file/directory name */ + const FF_TCHAR* path, /* Pointer to the file/directory name */ const FF_FILINFO* fno /* Pointer to the timestamp to be set */ ) { @@ -5272,8 +5272,8 @@ FRESULT f_utime ( /*-----------------------------------------------------------------------*/ FRESULT f_getlabel ( - const TCHAR* path, /* Logical drive number */ - TCHAR* label, /* Buffer to store the volume label */ + const FF_TCHAR* path, /* Logical drive number */ + FF_TCHAR* label, /* Buffer to store the volume label */ DWORD* vsn /* Variable to store the volume serial number */ ) { @@ -5322,7 +5322,7 @@ FRESULT f_getlabel ( if (wc == 0) { di = 0; break; } /* Invalid char in current code page? */ di += put_utf(wc, &label[di], 4); /* Store it in Unicode */ #else /* ANSI/OEM output */ - label[di++] = (TCHAR)wc; + label[di++] = (FF_TCHAR)wc; #endif } do { /* Truncate trailing spaces */ @@ -5369,7 +5369,7 @@ FRESULT f_getlabel ( /*-----------------------------------------------------------------------*/ FRESULT f_setlabel ( - const TCHAR* label /* Volume label to set with heading logical drive number */ + const FF_TCHAR* label /* Volume label to set with heading logical drive number */ ) { FRESULT res; @@ -5800,7 +5800,7 @@ static FRESULT create_partition ( FRESULT f_mkfs ( - const TCHAR* path, /* Logical drive number */ + const FF_TCHAR* path, /* Logical drive number */ const FF_MKFS_PARM* opt, /* Format options */ void* work, /* Pointer to working buffer (null: use heap memory) */ UINT len /* Size of working buffer [byte] */ @@ -6335,14 +6335,14 @@ FRESULT f_fdisk ( /* Get a String from the File */ /*-----------------------------------------------------------------------*/ -TCHAR* f_gets ( - TCHAR* buff, /* Pointer to the buffer to store read string */ +FF_TCHAR* f_gets ( + FF_TCHAR* buff, /* Pointer to the buffer to store read string */ int len, /* Size of string buffer (items) */ FF_FIL* fp /* Pointer to the file object */ ) { int nc = 0; - TCHAR *p = buff; + FF_TCHAR *p = buff; BYTE s[4]; UINT rc; DWORD dc; @@ -6407,32 +6407,32 @@ TCHAR* f_gets ( if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */ #if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */ if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */ - *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */ + *p++ = (FF_TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */ dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */ } - *p++ = (TCHAR)dc; nc++; + *p++ = (FF_TCHAR)dc; nc++; if (dc == '\n') break; /* End of line? */ #elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ if (dc < 0x80) { /* Single byte? */ - *p++ = (TCHAR)dc; + *p++ = (FF_TCHAR)dc; nc++; if (dc == '\n') break; /* End of line? */ } else { if (dc < 0x800) { /* 2-byte sequence? */ - *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); - *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + *p++ = (FF_TCHAR)(0xC0 | (dc >> 6 & 0x1F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F)); nc += 2; } else { if (dc < 0x10000) { /* 3-byte sequence? */ - *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); - *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); - *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + *p++ = (FF_TCHAR)(0xE0 | (dc >> 12 & 0x0F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F)); nc += 3; } else { /* 4-byte sequence? */ - *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); - *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); - *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); - *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + *p++ = (FF_TCHAR)(0xF0 | (dc >> 18 & 0x07)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 12 & 0x3F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F)); nc += 4; } } @@ -6447,7 +6447,7 @@ TCHAR* f_gets ( if (rc != 1) break; /* EOF? */ dc = s[0]; if (FF_USE_STRFUNC == 2 && dc == '\r') continue; - *p++ = (TCHAR)dc; nc++; + *p++ = (FF_TCHAR)dc; nc++; if (dc == '\n') break; } #endif @@ -6485,7 +6485,7 @@ typedef struct { /* Buffered file write with code conversion */ -static void putc_bfd (putbuff* pb, TCHAR c) +static void putc_bfd (putbuff* pb, FF_TCHAR c) { UINT n; int i, nc; @@ -6493,7 +6493,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) WCHAR hs, wc; #if FF_LFN_UNICODE == 2 DWORD dc; - const TCHAR *tp; + const FF_TCHAR *tp; #endif #endif @@ -6535,7 +6535,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) return; } } - tp = (const TCHAR*)pb->bs; + tp = (const FF_TCHAR*)pb->bs; dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ if (dc == 0xFFFFFFFF) return; /* Wrong code? */ wc = (WCHAR)dc; @@ -6638,7 +6638,7 @@ static void putc_init (putbuff* pb, FF_FIL* fp) int f_putc ( - TCHAR c, /* A character to be output */ + FF_TCHAR c, /* A character to be output */ FF_FIL* fp /* Pointer to the file object */ ) { @@ -6658,7 +6658,7 @@ int f_putc ( /*-----------------------------------------------------------------------*/ int f_puts ( - const TCHAR* str, /* Pointer to the string to be output */ + const FF_TCHAR* str, /* Pointer to the string to be output */ FF_FIL* fp /* Pointer to the file object */ ) { @@ -6727,7 +6727,7 @@ static void ftoa ( char* buf, /* Buffer to output the floating point string */ double val, /* Value to output */ int prec, /* Number of fractional digits */ - TCHAR fmt /* Notation */ + FF_TCHAR fmt /* Notation */ ) { int d; @@ -6800,7 +6800,7 @@ static void ftoa ( int f_printf ( FF_FIL* fp, /* Pointer to the file object */ - const TCHAR* fmt, /* Pointer to the format string */ + const FF_TCHAR* fmt, /* Pointer to the format string */ ... /* Optional arguments... */ ) { @@ -6813,8 +6813,8 @@ int f_printf ( #else DWORD v; #endif - TCHAR tc, pad, *tp; - TCHAR nul = 0; + FF_TCHAR tc, pad, *tp; + FF_TCHAR nul = 0; char d, str[SZ_NUM_BUF]; @@ -6879,10 +6879,10 @@ int f_printf ( case 'X': /* Unsigned hexdecimal (upper case) */ r = 16; break; case 'c': /* Character */ - putc_bfd(&pb, (TCHAR)va_arg(arp, int)); + putc_bfd(&pb, (FF_TCHAR)va_arg(arp, int)); continue; case 's': /* String */ - tp = va_arg(arp, TCHAR*); /* Get a pointer argument */ + tp = va_arg(arp, FF_TCHAR*); /* Get a pointer argument */ if (!tp) tp = &nul; /* Null ptr generates a null string */ for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */ if (prec >= 0 && j > (UINT)prec) j = prec; /* Limited length of string body */ @@ -6937,7 +6937,7 @@ int f_printf ( if (f & 1) str[i++] = '-'; /* Sign */ /* Write it */ for (j = i; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ - do putc_bfd(&pb, (TCHAR)str[--i]); while (i); /* Body */ + do putc_bfd(&pb, (FF_TCHAR)str[--i]); while (i); /* Body */ while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ } diff --git a/src/fatfs/ff.h b/src/fatfs/ff.h index a8c34aa4..1662d836 100644 --- a/src/fatfs/ff.h +++ b/src/fatfs/ff.h @@ -85,24 +85,24 @@ typedef DWORD LBA_t; -/* Type of path name strings on FatFs API (TCHAR) */ +/* Type of path name strings on FatFs API (FF_TCHAR) */ #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ -typedef WCHAR TCHAR; +typedef WCHAR FF_TCHAR; #define _T(x) L ## x #define _TEXT(x) L ## x #elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ -typedef char TCHAR; +typedef char FF_TCHAR; #define _T(x) u8 ## x #define _TEXT(x) u8 ## x #elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ -typedef DWORD TCHAR; +typedef DWORD FF_TCHAR; #define _T(x) U ## x #define _TEXT(x) U ## x #elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) #error Wrong FF_LFN_UNICODE setting #else /* ANSI/OEM code in SBCS/DBCS */ -typedef char TCHAR; +typedef char FF_TCHAR; #define _T(x) x #define _TEXT(x) x #endif @@ -236,7 +236,7 @@ typedef struct { DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ #endif #if FF_USE_FIND - const TCHAR* pat; /* Pointer to the name matching pattern */ + const FF_TCHAR* pat; /* Pointer to the name matching pattern */ #endif } FF_DIR; @@ -250,10 +250,10 @@ typedef struct { WORD ftime; /* Modified time */ BYTE fattrib; /* File attribute */ #if FF_USE_LFN - TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ - TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ + FF_TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ + FF_TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ #else - TCHAR fname[12 + 1]; /* File name */ + FF_TCHAR fname[12 + 1]; /* File name */ #endif } FF_FILINFO; @@ -301,40 +301,40 @@ typedef enum { /*--------------------------------------------------------------*/ /* FatFs module application interface */ -FRESULT f_open (FF_FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_open (FF_FIL* fp, const FF_TCHAR* path, BYTE mode); /* Open or create a file */ FRESULT f_close (FF_FIL* fp); /* Close an open file object */ FRESULT f_read (FF_FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ FRESULT f_write (FF_FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ FRESULT f_lseek (FF_FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ FRESULT f_truncate (FF_FIL* fp); /* Truncate the file */ FRESULT f_sync (FF_FIL* fp); /* Flush cached data of the writing file */ -FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_opendir (FF_DIR* dp, const FF_TCHAR* path); /* Open a directory */ FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */ FRESULT f_readdir (FF_DIR* dp, FF_FILINFO* fno); /* Read a directory item */ -FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const FF_TCHAR* path, const FF_TCHAR* pattern); /* Find first file */ FRESULT f_findnext (FF_DIR* dp, FF_FILINFO* fno); /* Find next file */ -FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ -FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ -FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ -FRESULT f_stat (const TCHAR* path, FF_FILINFO* fno); /* Get file status */ -FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ -FRESULT f_utime (const TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */ -FRESULT f_chdir (const TCHAR* path); /* Change current directory */ -FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ -FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ -FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ -FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ -FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_mkdir (const FF_TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const FF_TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const FF_TCHAR* path_old, const FF_TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const FF_TCHAR* path, FF_FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const FF_TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const FF_TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const FF_TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const FF_TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (FF_TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const FF_TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const FF_TCHAR* path, FF_TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const FF_TCHAR* label); /* Set volume label */ FRESULT f_forward (FF_FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ FRESULT f_expand (FF_FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */ -FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_mount (FATFS* fs, const FF_TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const FF_TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */ FRESULT f_setcp (WORD cp); /* Set current code page */ -int f_putc (TCHAR c, FF_FIL* fp); /* Put a character to the file */ -int f_puts (const TCHAR* str, FF_FIL* cp); /* Put a string to the file */ -int f_printf (FF_FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ -TCHAR* f_gets (TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */ +int f_putc (FF_TCHAR c, FF_FIL* fp); /* Put a character to the file */ +int f_puts (const FF_TCHAR* str, FF_FIL* cp); /* Put a string to the file */ +int f_printf (FF_FIL* fp, const FF_TCHAR* str, ...); /* Put a formatted string to the file */ +FF_TCHAR* f_gets (FF_TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */ #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) @@ -414,16 +414,6 @@ int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ - -// extra additions for interfacing with melonDS - -typedef UINT (*ff_disk_read_cb)(BYTE* buff, LBA_t sector, UINT count); -typedef UINT (*ff_disk_write_cb)(BYTE* buff, LBA_t sector, UINT count); - -void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt); -void ff_disk_close(void); - - #ifdef __cplusplus } #endif diff --git a/src/fatfs/ffsystem.c b/src/fatfs/ffsystem.c index 63fedf65..ebde84a5 100644 --- a/src/fatfs/ffsystem.c +++ b/src/fatfs/ffsystem.c @@ -110,7 +110,11 @@ DWORD get_fattime(void) time_t timestamp = time(NULL); struct tm timedata; +#if defined(_MSC_VER) + localtime_s(&timedata, ×tamp); +#else localtime_r(×tamp, &timedata); +#endif DWORD ret; ret = (timedata.tm_sec >> 1); diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 51f8f613..6f09d4b7 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,8 +24,13 @@ #include #include +namespace melonDS +{ +class NDS; +} namespace Frontend { +using namespace melonDS; enum ScreenLayout { @@ -104,14 +109,14 @@ int AudioOut_GetNumSamples(int outlen); void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume); // feed silence to the microphone input -void Mic_FeedSilence(); +void Mic_FeedSilence(NDS& nds); // feed random noise to the microphone input -void Mic_FeedNoise(); +void Mic_FeedNoise(NDS& nds); // feed an external buffer to the microphone input // buffer should be mono -void Mic_FeedExternalBuffer(); +void Mic_FeedExternalBuffer(NDS& nds); void Mic_SetExternalBuffer(s16* buffer, u32 len); } diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index fc0edebc..25e04db3 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -27,6 +27,7 @@ #include "mic_blow.h" +using namespace melonDS; namespace Frontend { @@ -62,32 +63,39 @@ int AudioOut_GetNumSamples(int outlen) void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume) { - float res_incr = inlen / (float)outlen; - float res_timer = 0; - int res_pos = 0; + double factor = (double) inlen / (double) outlen; + double inpos = -(factor / 2); + double vol = (double) volume / 256.f; - for (int i = 0; i < outlen; i++) + for (int i = 0; i < outlen * 2; i += 2) { - outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; - outbuf[i*2+1] = (inbuf[res_pos*2+1] * volume) >> 8; + double intpart_d; + double frac = modf(inpos, &intpart_d); + int intpart = (int) intpart_d; - res_timer += res_incr; - while (res_timer >= 1.0) - { - res_timer -= 1.0; - res_pos++; - } + double l1 = inbuf[ intpart * 2]; + double l2 = inbuf[(intpart * 2) + 2]; + double r1 = inbuf[(intpart * 2) + 1]; + double r2 = inbuf[(intpart * 2) + 3]; + + double ldiff = l2 - l1; + double rdiff = r2 - r1; + + outbuf[i] = (s16) round((l1 + ldiff * frac) * vol); + outbuf[i+1] = (s16) round((r1 + rdiff * frac) * vol); + + inpos += factor; } } -void Mic_FeedSilence() +void Mic_FeedSilence(NDS& nds) { MicBufferReadPos = 0; - NDS::MicInputFrame(NULL, 0); + nds.MicInputFrame(NULL, 0); } -void Mic_FeedNoise() +void Mic_FeedNoise(NDS& nds) { int sample_len = sizeof(mic_blow) / sizeof(u16); static int sample_pos = 0; @@ -101,12 +109,12 @@ void Mic_FeedNoise() if (sample_pos >= sample_len) sample_pos = 0; } - NDS::MicInputFrame(tmp, 735); + nds.MicInputFrame(tmp, 735); } -void Mic_FeedExternalBuffer() +void Mic_FeedExternalBuffer(NDS& nds) { - if (!MicBuffer) return Mic_FeedSilence(); + if (!MicBuffer) return Mic_FeedSilence(nds); if ((MicBufferReadPos + 735) > MicBufferLength) { @@ -115,12 +123,12 @@ void Mic_FeedExternalBuffer() memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16)); memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16)); - NDS::MicInputFrame(tmp, 735); + nds.MicInputFrame(tmp, 735); MicBufferReadPos = 735 - len1; } else { - NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735); + nds.MicInputFrame(&MicBuffer[MicBufferReadPos], 735); MicBufferReadPos += 735; } } diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp index e4c49e91..e772be9a 100644 --- a/src/frontend/Util_Video.cpp +++ b/src/frontend/Util_Video.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -471,7 +471,7 @@ int GetScreenTransforms(float* out, int* kind) bool GetTouchCoords(int& x, int& y, bool clamp) { - if (HybEnable && HybScreen == 1) + if (HybEnable && HybScreen == 1) { float vx = x; float vy = y; @@ -487,7 +487,7 @@ bool GetTouchCoords(int& x, int& y, bool clamp) { x = std::clamp((int)vx, 0, 255); y = std::clamp((int)vy, 0, 191); - + return true; } if (HybPrevTouchScreen == 2) diff --git a/src/frontend/duckstation/gl/context.cpp b/src/frontend/duckstation/gl/context.cpp index a0a4183b..308b3c40 100644 --- a/src/frontend/duckstation/gl/context.cpp +++ b/src/frontend/duckstation/gl/context.cpp @@ -3,11 +3,7 @@ #include "loader.h" #include #include -#ifdef __APPLE__ #include -#else -#include -#endif Log_SetChannel(GL::Context); #if defined(_WIN32) diff --git a/src/frontend/duckstation/gl/context.h b/src/frontend/duckstation/gl/context.h index 41d8a2cd..06e0bc5e 100644 --- a/src/frontend/duckstation/gl/context.h +++ b/src/frontend/duckstation/gl/context.h @@ -6,6 +6,7 @@ #include namespace GL { +using namespace melonDS; class Context { public: diff --git a/src/frontend/duckstation/gl/context_agl.h b/src/frontend/duckstation/gl/context_agl.h index 459bf2fd..4ae5202d 100644 --- a/src/frontend/duckstation/gl/context_agl.h +++ b/src/frontend/duckstation/gl/context_agl.h @@ -13,6 +13,7 @@ struct NSView; namespace GL { +using namespace melonDS; class ContextAGL final : public Context { public: diff --git a/src/frontend/duckstation/gl/context_wgl.cpp b/src/frontend/duckstation/gl/context_wgl.cpp index 03c18e87..09f994c9 100644 --- a/src/frontend/duckstation/gl/context_wgl.cpp +++ b/src/frontend/duckstation/gl/context_wgl.cpp @@ -3,6 +3,7 @@ #include "../log.h" #include "../scoped_guard.h" #include "loader.h" +using namespace melonDS; Log_SetChannel(GL::ContextWGL); // TODO: get rid of this diff --git a/src/frontend/duckstation/gl/x11_window.h b/src/frontend/duckstation/gl/x11_window.h index aff38b7b..e216a058 100644 --- a/src/frontend/duckstation/gl/x11_window.h +++ b/src/frontend/duckstation/gl/x11_window.h @@ -4,6 +4,7 @@ #include namespace GL { +using namespace melonDS; class X11Window { public: diff --git a/src/frontend/duckstation/window_info.h b/src/frontend/duckstation/window_info.h index 44912caa..43526172 100644 --- a/src/frontend/duckstation/window_info.h +++ b/src/frontend/duckstation/window_info.h @@ -28,8 +28,8 @@ struct WindowInfo Type type = Type::Surfaceless; void* display_connection = nullptr; void* window_handle = nullptr; - u32 surface_width = 0; - u32 surface_height = 0; + melonDS::u32 surface_width = 0; + melonDS::u32 surface_height = 0; float surface_refresh_rate = 0.0f; float surface_scale = 1.0f; SurfaceFormat surface_format = SurfaceFormat::RGB8; diff --git a/src/frontend/mic_blow.h b/src/frontend/mic_blow.h index 2fdd9452..f8dbc107 100644 --- a/src/frontend/mic_blow.h +++ b/src/frontend/mic_blow.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,2608 +19,2610 @@ #ifndef MIC_BLOW_H #define MIC_BLOW_H -const u16 mic_blow[] = +#include "types.h" + +const melonDS::u16 mic_blow[] = { - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0004, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0001, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0001, 0x0000, 0x0007, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x18CE, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, - 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0001, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0004, 0x0001, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x847B, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, - 0x0002, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0349, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0009, 0x0000, 0x0003, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x8799, 0x651E, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0xA53E, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xC6FA, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0009, - 0x0000, 0x0008, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0003, 0xB631, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xCBB5, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0006, 0x9D85, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x39BA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0x7D89, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, - 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, - 0x8E09, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6615, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x1CD9, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0009, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, - 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x4D8C, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0006, 0x0000, 0x0008, 0x0000, - 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0002, - 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xDB0F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x8888, 0x0000, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x98DC, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xABF2, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, - 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0xAEAB, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x1B34, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x5D05, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x6781, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0002, 0x0002, 0x0003, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, - 0x0004, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, - 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x2892, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0001, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x22BC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x312D, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, - 0x0002, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x4606, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xF9FF, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0002, 0x0004, 0x0002, 0x0003, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0001, - 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xED4F, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFF9, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xEFB5, 0x0003, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, - 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFE, - 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x32F8, 0x0001, 0x0002, 0x7052, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0x2D60, 0x0004, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0x1DA1, 0x0004, 0x0002, 0x0003, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0007, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0xF528, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xBF77, 0x0001, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0002, 0x0004, 0x0003, 0x0002, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0x2365, 0x0000, 0x0001, 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, - 0x0005, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, - 0x0005, 0x0000, 0x0005, 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x9340, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, - 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0003, - 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF8, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFF9, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0003, 0x0001, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0x0000, 0x0003, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0003, 0x0000, - 0x0006, 0x0000, 0x0004, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0003, - 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0002, 0xC55F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0xACC1, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, - 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0xEE35, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0001, 0x0003, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0001, 0x0003, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0x0003, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0F4C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, - 0xED13, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFA, 0xA5B7, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x06FD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFB, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x1187, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, - 0x0000, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xFFFC, 0xFFFF, 0xFFF7, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0004, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xEB31, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF7, 0xFFFF, - 0xFFF4, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFD8B, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x69E7, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0xF443, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x52D6, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, - 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xD7B3, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x021D, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0001, 0x0005, 0x0000, 0xA271, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x08DF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, - 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x000A, 0x0000, 0x0009, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFE, 0xFFFB, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, - 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xCB6A, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0005, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xEFC1, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0006, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0xBDC8, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x5B74, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0004, 0x0002, 0x0002, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, - 0x0003, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0003, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x2C65, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0xFFF9, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0002, 0x0001, 0x0003, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, - 0x0002, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0009, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0007, - 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x000B, 0x0000, 0x000A, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xCBB6, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0003, 0x0001, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0009, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x6BE6, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0002, 0x1FBC, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0x39B7, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0005, - 0x0000, 0x0007, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, - 0xFFFE, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0004, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0005, 0x0002, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, - 0xE8FD, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x5B7E, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0x1AE5, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xBD30, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x9A4C, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, 0x0000, 0x0006, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xF5EE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0x724C, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x3F1F, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0002, 0x0002, 0x0004, 0x0002, 0x0003, - 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x8AD2, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0xBEDD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0x946E, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xC7B0, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0x851A, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, - 0x0005, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0x0001, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, - 0x0001, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xA54E, 0x0002, 0x0000, 0x7C4D, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0002, 0x0001, 0x0002, - 0x0003, 0x0002, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xADFD, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0x95DE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0005, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xA0E0, 0x59A3, 0x4A73, 0x5D3E, - 0x7620, 0x77DB, 0x48DC, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xBB82, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0004, 0x1D09, 0x92A8, 0x72D7, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x020F, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0001, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x63F7, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x293D, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, - 0x0000, 0x0009, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0xA433, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0807, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x098A, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x3BF1, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0002, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x4159, - 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0001, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x1905, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0x9129, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0004, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, - 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x128F, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0xA307, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, - 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0xE5A4, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xC526, 0x0000, - 0x0005, 0x519E, 0xE4F3, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xA418, 0x861F, 0xE13B, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xCC64, 0x0001, 0x0000, 0x0002, 0xDE8B, 0xFFFD, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFE, 0x4AA5, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x5494, 0x57CB, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0005, - 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xD476, 0x0000, 0x0007, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0000, 0x60E6, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x457D, 0x0004, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, - 0x0000, 0x0007, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, - 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0x3004, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, - 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0xDAA3, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, - 0x0000, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x9C09, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xC23E, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x3259, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFCBD, 0x0000, 0x0001, - 0x0004, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0003, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0x0005, 0x0000, 0x0003, 0x0003, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0005, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0003, 0x0000, 0x0007, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0009, 0x0000, 0x0008, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xE550, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFF9, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xA807, 0x0001, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x2CF7, 0x74D1, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0002, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x000B, 0x0000, 0x0006, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0003, - 0x0003, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xCF32, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, - 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0001, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFD, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0002, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xE0F0, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xBF1C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0001, 0x20F1, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xA727, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xC285, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xD192, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0002, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x90D5, 0xFFFF, 0xFFFC, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFF7, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0002, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0003, 0x0000, 0x0008, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x7420, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, - 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0003, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x439F, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x90B5, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x6CD1, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x75D6, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0x7342, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0x9B49, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x5542, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x0002, 0x0001, 0x0005, 0x0001, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0000, 0xE0F8, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xD0C7, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xC8CF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xF913, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xDE55, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xF6F5, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0002, 0x0001, 0x0004, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, - 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x524D, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, - 0x0000, 0x0008, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0xE4B8, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0007, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0001, 0x0002, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0001, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0002, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x1DA1, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xA67D, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x1ED6, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x23F1, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, - 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0x0002, 0x0001, 0x0000, 0x0004, - 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, - 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, - 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, - 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, - 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x7FFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xD88D, 0x0003, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, - 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFC, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0003, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFEBA, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0009, 0x0000, 0x0005, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xBE46, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, - 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0x7B80, 0x0000, 0xF493, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xA5C4, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xBB34, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFC, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, - 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, - 0x0003, 0x0002, 0x0000, 0x0003, 0x9587, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x000A, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0002, 0x0000, 0x0007, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, - 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFD, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0003, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0004, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, - 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xED3A, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFE1, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0003, - 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x926E, 0xFFFF, 0xFFFC, 0x8C84, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x7AD7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x49BC, 0x0003, - 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, - 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x09CB, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0002, - 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x19CE, 0xCF98, 0xEC10, 0x74E9, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, - 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0005, 0x0000, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x22E2, 0x0008, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0001, 0x0007, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0xEC7E, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x6687, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x000A, 0x0000, 0x0008, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0005, 0x0002, - 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0005, 0x8A68, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, - 0x1C49, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x7B27, 0x5A5C, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0004, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0001, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0001, 0x0000, 0x0007, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x18CE, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, + 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0001, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0004, 0x0001, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x847B, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, + 0x0002, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0349, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0009, 0x0000, 0x0003, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x8799, 0x651E, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0xA53E, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xC6FA, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0009, + 0x0000, 0x0008, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0003, 0xB631, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xCBB5, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0006, 0x9D85, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x39BA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0x7D89, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, + 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, + 0x8E09, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6615, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x1CD9, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0009, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, + 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x4D8C, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0006, 0x0000, 0x0008, 0x0000, + 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0002, + 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xDB0F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x8888, 0x0000, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x98DC, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xABF2, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, + 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0xAEAB, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x1B34, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x5D05, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x6781, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0002, 0x0002, 0x0003, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, + 0x0004, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, + 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x2892, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0001, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x22BC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x312D, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, + 0x0002, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x4606, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xF9FF, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0002, 0x0004, 0x0002, 0x0003, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0001, + 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xED4F, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFF9, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xEFB5, 0x0003, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, + 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFE, + 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x32F8, 0x0001, 0x0002, 0x7052, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0x2D60, 0x0004, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0x1DA1, 0x0004, 0x0002, 0x0003, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0007, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0xF528, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xBF77, 0x0001, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0002, 0x0004, 0x0003, 0x0002, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0x2365, 0x0000, 0x0001, 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, + 0x0005, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, + 0x0005, 0x0000, 0x0005, 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x9340, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, + 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0003, + 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF8, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFF9, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0003, 0x0001, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0x0000, 0x0003, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0003, 0x0000, + 0x0006, 0x0000, 0x0004, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0003, + 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0002, 0xC55F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0xACC1, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, + 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0xEE35, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0001, 0x0003, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0001, 0x0003, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0x0003, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0F4C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, + 0xED13, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFA, 0xA5B7, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x06FD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFB, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x1187, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, + 0x0000, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xFFFC, 0xFFFF, 0xFFF7, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0004, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xEB31, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF7, 0xFFFF, + 0xFFF4, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFD8B, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x69E7, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0xF443, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x52D6, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, + 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xD7B3, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x021D, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0001, 0x0005, 0x0000, 0xA271, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x08DF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, + 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x000A, 0x0000, 0x0009, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFE, 0xFFFB, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, + 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xCB6A, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0005, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xEFC1, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0006, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0xBDC8, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x5B74, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0004, 0x0002, 0x0002, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, + 0x0003, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0003, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x2C65, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0xFFF9, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0002, 0x0001, 0x0003, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, + 0x0002, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0009, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0007, + 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x000B, 0x0000, 0x000A, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xCBB6, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0003, 0x0001, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0009, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x6BE6, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0002, 0x1FBC, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0x39B7, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0005, + 0x0000, 0x0007, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, + 0xFFFE, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0004, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0005, 0x0002, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, + 0xE8FD, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x5B7E, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0x1AE5, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xBD30, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x9A4C, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, 0x0000, 0x0006, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xF5EE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0x724C, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x3F1F, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0002, 0x0002, 0x0004, 0x0002, 0x0003, + 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x8AD2, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0xBEDD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0x946E, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xC7B0, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0x851A, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, + 0x0005, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0x0001, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, + 0x0001, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xA54E, 0x0002, 0x0000, 0x7C4D, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0002, 0x0001, 0x0002, + 0x0003, 0x0002, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xADFD, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0x95DE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0005, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xA0E0, 0x59A3, 0x4A73, 0x5D3E, + 0x7620, 0x77DB, 0x48DC, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xBB82, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0004, 0x1D09, 0x92A8, 0x72D7, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x020F, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0001, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x63F7, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x293D, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, + 0x0000, 0x0009, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0xA433, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0807, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x098A, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x3BF1, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0002, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x4159, + 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x1905, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0x9129, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0004, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, + 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x128F, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0xA307, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, + 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0xE5A4, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xC526, 0x0000, + 0x0005, 0x519E, 0xE4F3, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xA418, 0x861F, 0xE13B, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xCC64, 0x0001, 0x0000, 0x0002, 0xDE8B, 0xFFFD, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFE, 0x4AA5, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x5494, 0x57CB, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0005, + 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xD476, 0x0000, 0x0007, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0000, 0x60E6, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x457D, 0x0004, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, + 0x0000, 0x0007, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, + 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0x3004, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, + 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0xDAA3, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, + 0x0000, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x9C09, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xC23E, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x3259, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFCBD, 0x0000, 0x0001, + 0x0004, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0003, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0x0005, 0x0000, 0x0003, 0x0003, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0005, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0003, 0x0000, 0x0007, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0009, 0x0000, 0x0008, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xE550, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFF9, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xA807, 0x0001, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x2CF7, 0x74D1, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0002, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x000B, 0x0000, 0x0006, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0003, + 0x0003, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xCF32, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, + 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0001, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFD, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0002, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xE0F0, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xBF1C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0001, 0x20F1, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xA727, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xC285, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xD192, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0002, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x90D5, 0xFFFF, 0xFFFC, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFF7, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0002, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0003, 0x0000, 0x0008, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x7420, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, + 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0003, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x439F, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x90B5, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x6CD1, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x75D6, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x7342, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0x9B49, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x5542, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x0002, 0x0001, 0x0005, 0x0001, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0000, 0xE0F8, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xD0C7, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xC8CF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xF913, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xDE55, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xF6F5, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0002, 0x0001, 0x0004, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, + 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x524D, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, + 0x0000, 0x0008, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0xE4B8, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0007, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0001, 0x0002, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0001, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0002, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x1DA1, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xA67D, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x1ED6, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x23F1, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, + 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0x0002, 0x0001, 0x0000, 0x0004, + 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, + 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, + 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, + 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x7FFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xD88D, 0x0003, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, + 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFC, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0003, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFEBA, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0009, 0x0000, 0x0005, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xBE46, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, + 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0x7B80, 0x0000, 0xF493, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xA5C4, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xBB34, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFC, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, + 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, + 0x0003, 0x0002, 0x0000, 0x0003, 0x9587, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x000A, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0002, 0x0000, 0x0007, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFD, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0003, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0004, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, + 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xED3A, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFE1, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0003, + 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x926E, 0xFFFF, 0xFFFC, 0x8C84, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x7AD7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x49BC, 0x0003, + 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, + 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x09CB, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0002, + 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x19CE, 0xCF98, 0xEC10, 0x74E9, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, + 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0005, 0x0000, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x22E2, 0x0008, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0001, 0x0007, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0xEC7E, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x6687, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x000A, 0x0000, 0x0008, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0005, 0x0002, + 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0005, 0x8A68, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, + 0x1C49, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x7B27, 0x5A5C, }; #endif // MIC_BLOW_H diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index b025aa8f..aa508e8d 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,7 @@ #include "ArchiveUtil.h" #include "Platform.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; @@ -119,13 +120,12 @@ QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteA } -u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize) +s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr& filedata, u32* filesize) { struct archive *a = archive_read_new(); struct archive_entry *entry; int r; - if (!filedata) return -1; archive_read_support_format_all(a); archive_read_support_filter_all(a); @@ -147,8 +147,8 @@ u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* size_t bytesToRead = archive_entry_size(entry); if (filesize) *filesize = bytesToRead; - *filedata = new u8[bytesToRead]; - ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead); + filedata = std::make_unique(bytesToRead); + ssize_t bytesRead = archive_read_data(a, filedata.get(), bytesToRead); archive_read_close(a); archive_read_free(a); diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index 761f5427..246670e7 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -35,8 +35,9 @@ namespace Archive { +using namespace melonDS; QVector ListArchive(QString path); -u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); +s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr& filedata, u32* filesize); //QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer); //u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); diff --git a/src/frontend/qt_sdl/AudioInOut.cpp b/src/frontend/qt_sdl/AudioInOut.cpp index 48e8a862..1f1ee1c5 100644 --- a/src/frontend/qt_sdl/AudioInOut.cpp +++ b/src/frontend/qt_sdl/AudioInOut.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,11 +22,13 @@ #include "FrontendUtil.h" #include "Config.h" +#include "NDS.h" #include "SPU.h" #include "Platform.h" #include "Input.h" #include "main.h" +using namespace melonDS; namespace AudioInOut { @@ -53,8 +55,9 @@ void AudioCallback(void* data, Uint8* stream, int len) s16 buf_in[1024*2]; int num_in; + EmuThread* emuThread = (EmuThread*)data; SDL_LockMutex(audioSyncLock); - num_in = SPU::ReadOutput(buf_in, len_in); + num_in = emuThread->NDS->SPU.ReadOutput(buf_in, len_in); SDL_CondSignal(audioSync); SDL_UnlockMutex(audioSyncLock); @@ -242,7 +245,7 @@ void MicLoadWav(const std::string& name) SDL_FreeWAV(buf); } -void MicProcess() +void MicProcess(melonDS::NDS& nds) { int type = Config::MicInputType; bool cmd = Input::HotkeyDown(HK_Mic); @@ -255,16 +258,16 @@ void MicProcess() switch (type) { case micInputType_Silence: // no mic - Frontend::Mic_FeedSilence(); + Frontend::Mic_FeedSilence(nds); break; case micInputType_External: // host mic case micInputType_Wav: // WAV - Frontend::Mic_FeedExternalBuffer(); + Frontend::Mic_FeedExternalBuffer(nds); break; case micInputType_Noise: // blowing noise - Frontend::Mic_FeedNoise(); + Frontend::Mic_FeedNoise(nds); break; } } @@ -294,7 +297,7 @@ void SetupMicInputData() } } -void Init() +void Init(EmuThread* thread) { audioMuted = false; audioSync = SDL_CreateCond(); @@ -308,6 +311,7 @@ void Init() whatIwant.channels = 2; whatIwant.samples = 1024; whatIwant.callback = AudioCallback; + whatIwant.userdata = thread; audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (!audioDevice) { @@ -347,12 +351,12 @@ void DeInit() micWavBuffer = nullptr; } -void AudioSync() +void AudioSync(NDS& nds) { if (audioDevice) { SDL_LockMutex(audioSyncLock); - while (SPU::GetOutputSize() > 1024) + while (nds.SPU.GetOutputSize() > 1024) { int ret = SDL_CondWaitTimeout(audioSync, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; @@ -361,11 +365,11 @@ void AudioSync() } } -void UpdateSettings() +void UpdateSettings(NDS& nds) { MicClose(); - SPU::SetInterpolation(Config::AudioInterp); + nds.SPU.SetInterpolation(static_cast(Config::AudioInterp)); SetupMicInputData(); MicOpen(); @@ -383,4 +387,4 @@ void Disable() MicClose(); } -} \ No newline at end of file +} diff --git a/src/frontend/qt_sdl/AudioInOut.h b/src/frontend/qt_sdl/AudioInOut.h index 76e0cb8b..0bf36540 100644 --- a/src/frontend/qt_sdl/AudioInOut.h +++ b/src/frontend/qt_sdl/AudioInOut.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -23,22 +23,27 @@ #include +class EmuThread; +namespace melonDS +{ +class NDS; +} namespace AudioInOut { -void Init(); +void Init(EmuThread* thread); void DeInit(); -void MicProcess(); +void MicProcess(melonDS::NDS& nds); void AudioMute(QMainWindow* mainWindow); -void AudioSync(); +void AudioSync(melonDS::NDS& nds); -void UpdateSettings(); +void UpdateSettings(melonDS::NDS& nds); void Enable(); void Disable(); } -#endif \ No newline at end of file +#endif diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index ef1f5717..8e08ef2b 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,18 +24,20 @@ #include "Platform.h" #include "Config.h" #include "NDS.h" +#include "DSi.h" #include "DSi_I2C.h" #include "AudioSettingsDialog.h" #include "ui_AudioSettingsDialog.h" +#include "main.h" - +using namespace melonDS; AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr; extern std::string EmuDirectory; -AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDialog(parent), ui(new Ui::AudioSettingsDialog) +AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive, EmuThread* emuThread) : QDialog(parent), ui(new Ui::AudioSettingsDialog), emuThread(emuThread) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -49,6 +51,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDia ui->cbInterpolation->addItem("Linear"); ui->cbInterpolation->addItem("Cosine"); ui->cbInterpolation->addItem("Cubic"); + ui->cbInterpolation->addItem("Gaussian (SNES)"); ui->cbInterpolation->setCurrentIndex(Config::AudioInterp); ui->cbBitDepth->addItem("Automatic"); @@ -63,7 +66,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDia ui->chkSyncDSiVolume->setChecked(Config::DSiVolumeSync); // Setup volume slider accordingly - if (emuActive && NDS::ConsoleType == 1) + if (emuActive && emuThread->NDS->ConsoleType == 1) { on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync); } @@ -77,15 +80,15 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDia const int count = SDL_GetNumAudioDevices(true); for (int i = 0; i < count; i++) { - ui->cbMic->addItem(SDL_GetAudioDeviceName(i, true)); + ui->cbMic->addItem(SDL_GetAudioDeviceName(i, true)); } if (Config::MicDevice == "" && count > 0) - { + { Config::MicDevice = SDL_GetAudioDeviceName(0, true); } - ui->cbMic->setCurrentText(QString::fromStdString(Config::MicDevice)); - + ui->cbMic->setCurrentText(QString::fromStdString(Config::MicDevice)); + grpMicMode = new QButtonGroup(this); grpMicMode->addButton(ui->rbMicNone, micInputType_Silence); grpMicMode->addButton(ui->rbMicExternal, micInputType_External); @@ -123,10 +126,11 @@ AudioSettingsDialog::~AudioSettingsDialog() void AudioSettingsDialog::onSyncVolumeLevel() { - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); bool state = ui->slVolume->blockSignals(true); - ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel()); + ui->slVolume->setValue(dsi.I2C.GetBPTWL()->GetVolumeLevel()); ui->slVolume->blockSignals(state); } } @@ -134,7 +138,7 @@ void AudioSettingsDialog::onSyncVolumeLevel() void AudioSettingsDialog::onConsoleReset() { on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync); - ui->chkSyncDSiVolume->setEnabled(NDS::ConsoleType == 1); + ui->chkSyncDSiVolume->setEnabled(emuThread->NDS->ConsoleType == 1); } void AudioSettingsDialog::on_AudioSettingsDialog_accepted() @@ -170,7 +174,7 @@ void AudioSettingsDialog::on_cbBitDepth_currentIndexChanged(int idx) void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx) { // prevent a spurious change - if (ui->cbInterpolation->count() < 4) return; + if (ui->cbInterpolation->count() < 5) return; Config::AudioInterp = ui->cbInterpolation->currentIndex(); @@ -179,9 +183,10 @@ void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx) void AudioSettingsDialog::on_slVolume_valueChanged(int val) { - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && emuThread->NDS->ConsoleType == 1) { - DSi_BPTWL::SetVolumeLevel(val); + auto& dsi = static_cast(*emuThread->NDS); + dsi.I2C.GetBPTWL()->SetVolumeLevel(val); return; } @@ -193,10 +198,11 @@ void AudioSettingsDialog::on_chkSyncDSiVolume_clicked(bool checked) Config::DSiVolumeSync = checked; bool state = ui->slVolume->blockSignals(true); - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); ui->slVolume->setMaximum(31); - ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel()); + ui->slVolume->setValue(dsi.I2C.GetBPTWL()->GetVolumeLevel()); ui->slVolume->setPageStep(4); ui->slVolume->setTickPosition(QSlider::TicksBelow); } diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h index aa994414..ced9bae9 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.h +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,17 +24,18 @@ namespace Ui { class AudioSettingsDialog; } class AudioSettingsDialog; +class EmuThread; class AudioSettingsDialog : public QDialog { Q_OBJECT public: - explicit AudioSettingsDialog(QWidget* parent, bool emuActive); + explicit AudioSettingsDialog(QWidget* parent, bool emuActive, EmuThread* emuThread); ~AudioSettingsDialog(); static AudioSettingsDialog* currentDlg; - static AudioSettingsDialog* openDlg(QWidget* parent, bool emuActive) + static AudioSettingsDialog* openDlg(QWidget* parent, bool emuActive, EmuThread* emuThread) { if (currentDlg) { @@ -42,7 +43,7 @@ public: return currentDlg; } - currentDlg = new AudioSettingsDialog(parent, emuActive); + currentDlg = new AudioSettingsDialog(parent, emuActive, emuThread); currentDlg->show(); return currentDlg; } @@ -69,6 +70,7 @@ private slots: void on_btnMicWavBrowse_clicked(); private: + EmuThread* emuThread; Ui::AudioSettingsDialog* ui; int oldInterp; diff --git a/src/frontend/qt_sdl/CLI.cpp b/src/frontend/qt_sdl/CLI.cpp index 9c19d799..299ce65b 100644 --- a/src/frontend/qt_sdl/CLI.cpp +++ b/src/frontend/qt_sdl/CLI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 melonDS team + Copyright 2021-2023 melonDS team This file is part of melonDS. @@ -27,8 +27,8 @@ #include "CLI.h" #include "Platform.h" -using Platform::Log; -using Platform::LogLevel; +using melonDS::Platform::Log; +using melonDS::Platform::LogLevel; namespace CLI { diff --git a/src/frontend/qt_sdl/CLI.h b/src/frontend/qt_sdl/CLI.h index 8850fad2..4997e6a7 100644 --- a/src/frontend/qt_sdl/CLI.h +++ b/src/frontend/qt_sdl/CLI.h @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 melonDS team + Copyright 2021-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 24261030..1dec174b 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -5,8 +5,12 @@ include(FixInterfaceIncludes) set(SOURCES_QT_SDL main.cpp main_shaders.h + Screen.cpp + Window.cpp + EmuThread.cpp CheatsDialog.cpp Config.cpp + DateTimeDialog.cpp EmuSettingsDialog.cpp PowerManagement/PowerManagementDialog.cpp PowerManagement/resources/battery.qrc @@ -28,7 +32,6 @@ set(SOURCES_QT_SDL LAN_PCap.cpp LAN_Socket.cpp LocalMP.cpp - OSD.cpp OSD_shaders.h font.h Platform.cpp @@ -37,7 +40,7 @@ set(SOURCES_QT_SDL SaveManager.cpp CameraManager.cpp AudioInOut.cpp - + ArchiveUtil.h ArchiveUtil.cpp @@ -81,11 +84,11 @@ if (BUILD_STATIC) endif() pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2) -pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp) +pkg_check_modules(Slirp REQUIRED slirp) pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive) pkg_check_modules(Zstd REQUIRED IMPORTED_TARGET libzstd) -fix_interface_includes(PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive) +fix_interface_includes(PkgConfig::SDL2 PkgConfig::LibArchive) add_compile_definitions(ARCHIVE_SUPPORT_ENABLED) @@ -139,6 +142,7 @@ else() ) target_link_libraries(melonDS PRIVATE "${X11_LIBRARIES}" "${EGL_LIBRARIES}") target_include_directories(melonDS PRIVATE "${X11_INCLUDE_DIR}") + add_compile_definitions(QAPPLICATION_CLASS=QApplication) endif() @@ -156,14 +160,19 @@ else() target_include_directories(melonDS PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() target_link_libraries(melonDS PRIVATE core) -target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive PkgConfig::Zstd) +target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::LibArchive PkgConfig::Zstd) target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS}) -if (UNIX) - option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF) -elseif (WIN32) +target_include_directories(melonDS PRIVATE "${Slirp_INCLUDE_DIRS}") +target_link_libraries(melonDS PRIVATE "${Slirp_LINK_LIBRARIES}") + +if (WIN32) option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON) + if (PORTABLE) + target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE) + endif() + configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc") target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc") target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res") @@ -182,10 +191,6 @@ elseif (WIN32) set_target_properties(melonDS PROPERTIES LINK_FLAGS_DEBUG "-mconsole") endif() -if (PORTABLE) - target_compile_definitions(melonDS PRIVATE PORTABLE) -endif() - if (APPLE) target_sources(melonDS PRIVATE sem_timedwait.cpp) diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index 19cf8d4d..cc575d2c 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -16,9 +16,12 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include + #include "CameraManager.h" #include "Config.h" +using namespace melonDS; #if QT_VERSION >= 0x060000 @@ -144,6 +147,7 @@ CameraManager::~CameraManager() // save settings here? delete[] frameBuffer; + delete[] tempFrameBuffer; } void CameraManager::init() @@ -256,6 +260,12 @@ void CameraManager::init() if (camDevice) { camDevice->load(); + if (camDevice->status() == QCamera::LoadingStatus) + { + QEventLoop loop; + connect(camDevice, &QCamera::statusChanged, &loop, &QEventLoop::quit); + loop.exec(); + } const QList supported = camDevice->supportedViewfinderSettings(); bool good = false; diff --git a/src/frontend/qt_sdl/CameraManager.h b/src/frontend/qt_sdl/CameraManager.h index 6743d19e..882b051a 100644 --- a/src/frontend/qt_sdl/CameraManager.h +++ b/src/frontend/qt_sdl/CameraManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -89,11 +89,11 @@ public: void setXFlip(bool flip); - void captureFrame(u32* frame, int width, int height, bool yuv); + void captureFrame(melonDS::u32* frame, int width, int height, bool yuv); - void feedFrame(u32* frame, int width, int height, bool yuv); - void feedFrame_UYVY(u32* frame, int width, int height); - void feedFrame_NV12(u8* planeY, u8* planeUV, int width, int height); + void feedFrame(melonDS::u32* frame, int width, int height, bool yuv); + void feedFrame_UYVY(melonDS::u32* frame, int width, int height); + void feedFrame_NV12(melonDS::u8* planeY, melonDS::u8* planeUV, int width, int height); signals: void camStartSignal(); @@ -120,15 +120,15 @@ private: int frameWidth, frameHeight; bool frameFormatYUV; - u32* frameBuffer; - u32* tempFrameBuffer; + melonDS::u32* frameBuffer; + melonDS::u32* tempFrameBuffer; QMutex frameMutex; bool xFlip; - void copyFrame_Straight(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip, bool yuv); - void copyFrame_RGBtoYUV(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip); - void copyFrame_YUVtoRGB(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip); + void copyFrame_Straight(melonDS::u32* src, int swidth, int sheight, melonDS::u32* dst, int dwidth, int dheight, bool xflip, bool yuv); + void copyFrame_RGBtoYUV(melonDS::u32* src, int swidth, int sheight, melonDS::u32* dst, int dwidth, int dheight, bool xflip); + void copyFrame_YUVtoRGB(melonDS::u32* src, int swidth, int sheight, melonDS::u32* dst, int dwidth, int dheight, bool xflip); }; #endif // CAMERAMANAGER_H diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.cpp b/src/frontend/qt_sdl/CameraSettingsDialog.cpp index 1844e0fd..cf404173 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.cpp +++ b/src/frontend/qt_sdl/CameraSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -26,6 +26,7 @@ #include "CameraSettingsDialog.h" #include "ui_CameraSettingsDialog.h" +using namespace melonDS; CameraSettingsDialog* CameraSettingsDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.h b/src/frontend/qt_sdl/CameraSettingsDialog.h index 8572ac42..a740193a 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.h +++ b/src/frontend/qt_sdl/CameraSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CheatsDialog.cpp b/src/frontend/qt_sdl/CheatsDialog.cpp index 1da5c5e5..df687230 100644 --- a/src/frontend/qt_sdl/CheatsDialog.cpp +++ b/src/frontend/qt_sdl/CheatsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -29,6 +29,7 @@ #include "CheatsDialog.h" #include "ui_CheatsDialog.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/frontend/qt_sdl/CheatsDialog.h b/src/frontend/qt_sdl/CheatsDialog.h index c5a7e13d..ab2ac309 100644 --- a/src/frontend/qt_sdl/CheatsDialog.h +++ b/src/frontend/qt_sdl/CheatsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -27,8 +27,8 @@ #include "ARCodeFile.h" -Q_DECLARE_METATYPE(ARCodeList::iterator) -Q_DECLARE_METATYPE(ARCodeCatList::iterator) +Q_DECLARE_METATYPE(melonDS::ARCodeList::iterator) +Q_DECLARE_METATYPE(melonDS::ARCodeCatList::iterator) namespace Ui { class CheatsDialog; } class CheatsDialog; @@ -87,7 +87,7 @@ private slots: private: Ui::CheatsDialog* ui; - ARCodeFile* codeFile; + melonDS::ARCodeFile* codeFile; ARCodeChecker* codeChecker; }; diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 699f5307..0d54b7fb 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,7 @@ #include #include #include +#include #include "Platform.h" #include "Config.h" #include "GPU.h" @@ -26,6 +27,7 @@ namespace Config { +using namespace melonDS; int KeyMapping[12]; int JoyMapping[12]; @@ -61,6 +63,7 @@ bool GL_BetterPolygons; bool GL_HiresCoordinates; bool LimitFPS; +int MaxFPS; bool AudioSync; bool ShowOSD; @@ -141,6 +144,9 @@ bool MouseHide; int MouseHideSeconds; bool PauseLostFocus; +std::string UITheme; + +int64_t RTCOffset; bool DSBatteryLevelOkay; int DSiBatteryLevel; @@ -148,6 +154,14 @@ bool DSiBatteryCharging; bool DSiFullBIOSBoot; +#ifdef GDBSTUB_ENABLED +bool GdbEnabled; +int GdbPortARM7; +int GdbPortARM9; +bool GdbARM7BreakOnStartup; +bool GdbARM9BreakOnStartup; +#endif + CameraConfig Camera[2]; @@ -242,6 +256,7 @@ ConfigEntry ConfigFile[] = {"GL_HiresCoordinates", 1, &GL_HiresCoordinates, true, false}, {"LimitFPS", 1, &LimitFPS, true, false}, + {"MaxFPS", 0, &MaxFPS, 1000, false}, {"AudioSync", 1, &AudioSync, false}, {"ShowOSD", 1, &ShowOSD, true, false}, @@ -333,6 +348,9 @@ ConfigEntry ConfigFile[] = {"MouseHide", 1, &MouseHide, false, false}, {"MouseHideSeconds", 0, &MouseHideSeconds, 5, false}, {"PauseLostFocus", 1, &PauseLostFocus, false, false}, + {"UITheme", 2, &UITheme, (std::string)"", false}, + + {"RTCOffset", 3, &RTCOffset, (int64_t)0, true}, {"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true, true}, {"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF, true}, @@ -340,6 +358,14 @@ ConfigEntry ConfigFile[] = {"DSiFullBIOSBoot", 1, &DSiFullBIOSBoot, false, true}, +#ifdef GDBSTUB_ENABLED + {"GdbEnabled", 1, &GdbEnabled, false, false}, + {"GdbPortARM7", 0, &GdbPortARM7, 3334, true}, + {"GdbPortARM9", 0, &GdbPortARM9, 3333, true}, + {"GdbARM7BreakOnStartup", 1, &GdbARM7BreakOnStartup, false, true}, + {"GdbARM9BreakOnStartup", 1, &GdbARM9BreakOnStartup, false, true}, +#endif + // TODO!! // we need a more elegant way to deal with this {"Camera0_InputType", 0, &Camera[0].InputType, 0, false}, @@ -355,7 +381,7 @@ ConfigEntry ConfigFile[] = }; -void LoadFile(int inst) +bool LoadFile(int inst, int actualinst) { Platform::FileHandle* f; if (inst > 0) @@ -363,11 +389,17 @@ void LoadFile(int inst) char name[100] = {0}; snprintf(name, 99, kUniqueConfigFile, inst+1); f = Platform::OpenLocalFile(name, Platform::FileMode::ReadText); + + if (!Platform::CheckLocalFileWritable(name)) return false; } else + { f = Platform::OpenLocalFile(kConfigFile, Platform::FileMode::ReadText); - if (!f) return; + if (actualinst == 0 && !Platform::CheckLocalFileWritable(kConfigFile)) return false; + } + + if (!f) return true; char linebuf[1024]; char entryname[32]; @@ -393,6 +425,7 @@ void LoadFile(int inst) case 0: *(int*)entry->Value = strtol(entryval, NULL, 10); break; case 1: *(bool*)entry->Value = strtol(entryval, NULL, 10) ? true:false; break; case 2: *(std::string*)entry->Value = entryval; break; + case 3: *(int64_t*)entry->Value = strtoll(entryval, NULL, 10); break; } break; @@ -401,9 +434,10 @@ void LoadFile(int inst) } CloseFile(f); + return true; } -void Load() +bool Load() { for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++) @@ -413,14 +447,17 @@ void Load() case 0: *(int*)entry->Value = std::get(entry->Default); break; case 1: *(bool*)entry->Value = std::get(entry->Default); break; case 2: *(std::string*)entry->Value = std::get(entry->Default); break; + case 3: *(int64_t*)entry->Value = std::get(entry->Default); break; } } - - LoadFile(0); - + int inst = Platform::InstanceID(); + + bool ret = LoadFile(0, inst); if (inst > 0) - LoadFile(inst); + ret = LoadFile(inst, inst); + + return ret; } void Save() @@ -446,9 +483,10 @@ void Save() switch (entry->Type) { - case 0: Platform::FileWriteFormatted(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value); break; - case 1: Platform::FileWriteFormatted(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break; - case 2: Platform::FileWriteFormatted(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break; + case 0: Platform::FileWriteFormatted(f, "%s=%d\n", entry->Name, *(int*)entry->Value); break; + case 1: Platform::FileWriteFormatted(f, "%s=%d\n", entry->Name, *(bool*)entry->Value ? 1:0); break; + case 2: Platform::FileWriteFormatted(f, "%s=%s\n", entry->Name, (*(std::string*)entry->Value).c_str()); break; + case 3: Platform::FileWriteFormatted(f, "%s=%" PRId64 "\n", entry->Name, *(int64_t*)entry->Value); break; } } diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 9f3f6e43..1c30b627 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -57,9 +57,9 @@ namespace Config struct ConfigEntry { char Name[32]; - int Type; // 0=int 1=bool 2=string + int Type; // 0=int 1=bool 2=string 3=64bit int void* Value; // pointer to the value variable - std::variant Default; + std::variant Default; bool InstanceUnique; // whether the setting can exist individually for each instance in multiplayer }; @@ -106,6 +106,7 @@ extern bool GL_BetterPolygons; extern bool GL_HiresCoordinates; extern bool LimitFPS; +extern int MaxFPS; extern bool AudioSync; extern bool ShowOSD; @@ -185,6 +186,9 @@ extern bool EnableCheats; extern bool MouseHide; extern int MouseHideSeconds; extern bool PauseLostFocus; +extern std::string UITheme; + +extern int64_t RTCOffset; extern bool DSBatteryLevelOkay; extern int DSiBatteryLevel; @@ -194,8 +198,14 @@ extern bool DSiFullBIOSBoot; extern CameraConfig Camera[2]; +extern bool GdbEnabled; +extern int GdbPortARM7; +extern int GdbPortARM9; +extern bool GdbARM7BreakOnStartup; +extern bool GdbARM9BreakOnStartup; -void Load(); + +bool Load(); void Save(); } diff --git a/src/frontend/qt_sdl/DateTimeDialog.cpp b/src/frontend/qt_sdl/DateTimeDialog.cpp new file mode 100644 index 00000000..88ae6942 --- /dev/null +++ b/src/frontend/qt_sdl/DateTimeDialog.cpp @@ -0,0 +1,91 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include + +#include "types.h" +#include "Config.h" + +#include "DateTimeDialog.h" +#include "ui_DateTimeDialog.h" + +DateTimeDialog* DateTimeDialog::currentDlg = nullptr; + + +DateTimeDialog::DateTimeDialog(QWidget* parent) : QDialog(parent), ui(new Ui::DateTimeDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + QDateTime now = QDateTime::currentDateTime(); + customTime = now.addSecs(Config::RTCOffset); + + ui->chkChangeTime->setChecked(false); + ui->chkResetTime->setChecked(false); + + ui->lblCustomTime->setText(customTime.toString(ui->txtNewCustomTime->displayFormat())); + startTimer(1000); + + ui->txtNewCustomTime->setEnabled(ui->chkChangeTime->isChecked()); +} + +DateTimeDialog::~DateTimeDialog() +{ + delete ui; +} + +void DateTimeDialog::timerEvent(QTimerEvent* event) +{ + customTime = customTime.addSecs(1); + ui->lblCustomTime->setText(customTime.toString(ui->txtNewCustomTime->displayFormat())); +} + +void DateTimeDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + if (ui->chkChangeTime->isChecked()) + { + QDateTime now = QDateTime::currentDateTime(); + Config::RTCOffset = now.secsTo(ui->txtNewCustomTime->dateTime()); + } + else if (ui->chkResetTime->isChecked()) + Config::RTCOffset = 0; + + Config::Save(); + } + + QDialog::done(r); + + closeDlg(); +} + +void DateTimeDialog::on_chkChangeTime_clicked(bool checked) +{ + if (checked) ui->chkResetTime->setChecked(false); + ui->txtNewCustomTime->setEnabled(checked); +} + +void DateTimeDialog::on_chkResetTime_clicked(bool checked) +{ + if (checked) + { + ui->chkChangeTime->setChecked(false); + ui->txtNewCustomTime->setEnabled(false); + } +} diff --git a/src/frontend/qt_sdl/DateTimeDialog.h b/src/frontend/qt_sdl/DateTimeDialog.h new file mode 100644 index 00000000..22dabcd0 --- /dev/null +++ b/src/frontend/qt_sdl/DateTimeDialog.h @@ -0,0 +1,70 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef DATETIMEDIALOG_H +#define DATETIMEDIALOG_H + +#include +#include +#include + +namespace Ui {class DateTimeDialog; } +class DateTimeDialog; + +class DateTimeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DateTimeDialog(QWidget* parent); + ~DateTimeDialog(); + + static DateTimeDialog* currentDlg; + static DateTimeDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new DateTimeDialog(parent); + currentDlg->open(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +protected: + void timerEvent(QTimerEvent* event) override; + +private slots: + void done(int r); + + void on_chkChangeTime_clicked(bool checked); + void on_chkResetTime_clicked(bool checked); + +private: + Ui::DateTimeDialog* ui; + + QDateTime customTime; +}; + +#endif // DATETIMEDIALOG_H diff --git a/src/frontend/qt_sdl/DateTimeDialog.ui b/src/frontend/qt_sdl/DateTimeDialog.ui new file mode 100644 index 00000000..80b8d56e --- /dev/null +++ b/src/frontend/qt_sdl/DateTimeDialog.ui @@ -0,0 +1,148 @@ + + + DateTimeDialog + + + + 0 + 0 + 357 + 161 + + + + Date and time - melonDS + + + + + + Date and time + + + + + + Current value: + + + + + + + [placeholder] + + + + + + + + + + + + + + Change to: + + + + + + + + 0 + 0 + 0 + 2000 + 1 + 1 + + + + + 23 + 59 + 59 + 2099 + 12 + 31 + + + + + 0 + 0 + 0 + 2000 + 1 + 1 + + + + dd/MM/yyyy HH:mm:ss + + + true + + + + + + + Reset to system date and time + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DateTimeDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DateTimeDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 0bdbb5c7..ca9c6716 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -29,7 +29,8 @@ #include "EmuSettingsDialog.h" #include "ui_EmuSettingsDialog.h" -using namespace Platform; +using namespace melonDS::Platform; +using namespace melonDS; EmuSettingsDialog* EmuSettingsDialog::currentDlg = nullptr; @@ -89,7 +90,22 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new ui->spnJITMaximumBlockSize->setDisabled(true); #endif +#ifdef GDBSTUB_ENABLED + ui->cbGdbEnabled->setChecked(Config::GdbEnabled); + ui->intGdbPortA7->setValue(Config::GdbPortARM7); + ui->intGdbPortA9->setValue(Config::GdbPortARM9); + ui->cbGdbBOSA7->setChecked(Config::GdbARM7BreakOnStartup); + ui->cbGdbBOSA9->setChecked(Config::GdbARM9BreakOnStartup); +#else + ui->cbGdbEnabled->setDisabled(true); + ui->intGdbPortA7->setDisabled(true); + ui->intGdbPortA9->setDisabled(true); + ui->cbGdbBOSA7->setDisabled(true); + ui->cbGdbBOSA9->setDisabled(true); +#endif + on_chkEnableJIT_toggled(); + on_cbGdbEnabled_toggled(); on_chkExternalBIOS_toggled(); const int imgsizes[] = {256, 512, 1024, 2048, 4096, 0}; @@ -223,6 +239,12 @@ void EmuSettingsDialog::done(int r) bool dsiSDFolderSync = ui->cbDSiSDFolder->isChecked(); std::string dsiSDFolderPath = ui->txtDSiSDFolder->text().toStdString(); + bool gdbEnabled = ui->cbGdbEnabled->isChecked(); + int gdbPortA7 = ui->intGdbPortA7->value(); + int gdbPortA9 = ui->intGdbPortA9->value(); + bool gdbBOSA7 = ui->cbGdbBOSA7->isChecked(); + bool gdbBOSA9 = ui->cbGdbBOSA9->isChecked(); + if (consoleType != Config::ConsoleType || directBoot != Config::DirectBoot #ifdef JIT_ENABLED @@ -231,6 +253,13 @@ void EmuSettingsDialog::done(int r) || jitBranchOptimisations != Config::JIT_BranchOptimisations || jitLiteralOptimisations != Config::JIT_LiteralOptimisations || jitFastMemory != Config::JIT_FastMemory +#endif +#ifdef GDBSTUB_ENABLED + || gdbEnabled != Config::GdbEnabled + || gdbPortA7 != Config::GdbPortARM7 + || gdbPortA9 != Config::GdbPortARM9 + || gdbBOSA7 != Config::GdbARM7BreakOnStartup + || gdbBOSA9 != Config::GdbARM9BreakOnStartup #endif || externalBiosEnable != Config::ExternalBIOSEnable || bios9Path != Config::BIOS9Path @@ -285,13 +314,20 @@ void EmuSettingsDialog::done(int r) Config::DSiSDFolderSync = dsiSDFolderSync; Config::DSiSDFolderPath = dsiSDFolderPath; - #ifdef JIT_ENABLED +#ifdef JIT_ENABLED Config::JIT_Enable = jitEnable; Config::JIT_MaxBlockSize = jitMaxBlockSize; Config::JIT_BranchOptimisations = jitBranchOptimisations; Config::JIT_LiteralOptimisations = jitLiteralOptimisations; Config::JIT_FastMemory = jitFastMemory; - #endif +#endif +#ifdef GDBSTUB_ENABLED + Config::GdbEnabled = gdbEnabled; + Config::GdbPortARM7 = gdbPortA7; + Config::GdbPortARM9 = gdbPortA9; + Config::GdbARM7BreakOnStartup = gdbBOSA7; + Config::GdbARM9BreakOnStartup = gdbBOSA9; +#endif Config::ConsoleType = consoleType; Config::DirectBoot = directBoot; @@ -344,6 +380,12 @@ void EmuSettingsDialog::on_btnFirmwareBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to firmware file.\nPlease check file/folder write permissions."); + return; + } + updateLastBIOSFolder(file); ui->txtFirmwarePath->setText(file); @@ -400,6 +442,12 @@ void EmuSettingsDialog::on_btnDLDISDBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DLDI SD image.\nPlease check file/folder write permissions."); + return; + } + updateLastBIOSFolder(file); ui->txtDLDISDPath->setText(file); @@ -432,6 +480,13 @@ void EmuSettingsDialog::on_btnDSiFirmwareBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DSi firmware file.\nPlease check file/folder write permissions."); + return; + } + + updateLastBIOSFolder(file); ui->txtDSiFirmwarePath->setText(file); @@ -446,6 +501,13 @@ void EmuSettingsDialog::on_btnDSiNANDBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DSi NAND image.\nPlease check file/folder write permissions."); + return; + } + + updateLastBIOSFolder(file); ui->txtDSiNANDPath->setText(file); @@ -474,6 +536,12 @@ void EmuSettingsDialog::on_btnDSiSDBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DSi SD image.\nPlease check file/folder write permissions."); + return; + } + updateLastBIOSFolder(file); ui->txtDSiSDPath->setText(file); @@ -506,6 +574,31 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled() ui->chkJITFastMemory->setDisabled(disabled); #endif ui->spnJITMaximumBlockSize->setDisabled(disabled); + + on_cbGdbEnabled_toggled(); +} + +void EmuSettingsDialog::on_cbGdbEnabled_toggled() +{ +#ifdef GDBSTUB_ENABLED + bool disabled = !ui->cbGdbEnabled->isChecked(); + bool jitenable = ui->chkEnableJIT->isChecked(); + + if (jitenable && !disabled) { + ui->cbGdbEnabled->setChecked(false); + disabled = true; + } +#else + bool disabled = true; + bool jitenable = true; + ui->cbGdbEnabled->setChecked(false); +#endif + + ui->cbGdbEnabled->setDisabled(jitenable); + ui->intGdbPortA7->setDisabled(disabled); + ui->intGdbPortA9->setDisabled(disabled); + ui->cbGdbBOSA7->setDisabled(disabled); + ui->cbGdbBOSA9->setDisabled(disabled); } void EmuSettingsDialog::on_chkExternalBIOS_toggled() diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h index 6a796267..b53d090b 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.h +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -77,6 +77,8 @@ private slots: void on_chkEnableJIT_toggled(); void on_chkExternalBIOS_toggled(); + void on_cbGdbEnabled_toggled(); + private: void verifyFirmware(); diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui index b434bbe2..2746e1da 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.ui +++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui @@ -568,6 +568,101 @@ + + + Devtools + + + + + + ARM9 port + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + ARM7 port + + + + + + + Enable GDB stub + + + + + + + Note: melonDS must be restarted in order for these changes to have effect + + + + + + + Note: GDB stub cannot be used together with the JIT recompiler + + + + + + + Break on startup + + + + + + + 1000 + + + 65535 + + + 3333 + + + + + + + 1000 + + + 65535 + + + 3334 + + + + + + + Break on startup + + + + + @@ -590,7 +685,6 @@ - tabWidget cbxConsoleType chkDirectBoot chkExternalBIOS @@ -639,8 +733,8 @@ accept() - 257 - 349 + 266 + 379 157 @@ -655,8 +749,8 @@ reject() - 325 - 349 + 334 + 379 286 diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp new file mode 100644 index 00000000..d16aead4 --- /dev/null +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -0,0 +1,752 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "main.h" +#include "Input.h" +#include "AudioInOut.h" + +#include "types.h" +#include "version.h" + +#include "FrontendUtil.h" + +#include "Args.h" +#include "NDS.h" +#include "NDSCart.h" +#include "GBACart.h" +#include "GPU.h" +#include "SPU.h" +#include "Wifi.h" +#include "Platform.h" +#include "LocalMP.h" +#include "Config.h" +#include "RTC.h" +#include "DSi.h" +#include "DSi_I2C.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" + +#include "Savestate.h" + +#include "ROMManager.h" +//#include "ArchiveUtil.h" +//#include "CameraManager.h" + +//#include "CLI.h" + +// TODO: uniform variable spelling +using namespace melonDS; + +// TEMP +extern bool RunningSomething; +extern MainWindow* mainWindow; +extern int autoScreenSizing; +extern int videoRenderer; +extern bool videoSettingsDirty; + + +EmuThread::EmuThread(QObject* parent) : QThread(parent) +{ + EmuStatus = emuStatus_Exit; + EmuRunning = emuStatus_Paused; + EmuPauseStack = EmuPauseStackRunning; + RunningSomething = false; + + connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint())); + connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); + connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); + connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); + connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); + connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); + connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); + connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); + connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); + connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); + connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); +} + +std::unique_ptr EmuThread::CreateConsole( + std::unique_ptr&& ndscart, + std::unique_ptr&& gbacart +) noexcept +{ + auto arm7bios = ROMManager::LoadARM7BIOS(); + if (!arm7bios) + return nullptr; + + auto arm9bios = ROMManager::LoadARM9BIOS(); + if (!arm9bios) + return nullptr; + + auto firmware = ROMManager::LoadFirmware(Config::ConsoleType); + if (!firmware) + return nullptr; + +#ifdef JIT_ENABLED + JITArgs jitargs { + static_cast(Config::JIT_MaxBlockSize), + Config::JIT_LiteralOptimisations, + Config::JIT_BranchOptimisations, + Config::JIT_FastMemory, + }; +#endif + +#ifdef GDBSTUB_ENABLED + GDBArgs gdbargs { + static_cast(Config::GdbPortARM7), + static_cast(Config::GdbPortARM9), + Config::GdbARM7BreakOnStartup, + Config::GdbARM9BreakOnStartup, + }; +#endif + + NDSArgs ndsargs { + std::move(ndscart), + std::move(gbacart), + std::move(arm9bios), + std::move(arm7bios), + std::move(*firmware), +#ifdef JIT_ENABLED + Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt, +#else + std::nullopt, +#endif + static_cast(Config::AudioBitDepth), + static_cast(Config::AudioInterp), +#ifdef GDBSTUB_ENABLED + Config::GdbEnabled ? std::make_optional(gdbargs) : std::nullopt, +#else + std::nullopt, +#endif + }; + + if (Config::ConsoleType == 1) + { + auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); + if (!arm7ibios) + return nullptr; + + auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); + if (!arm9ibios) + return nullptr; + + auto nand = ROMManager::LoadNAND(*arm7ibios); + if (!nand) + return nullptr; + + auto sdcard = ROMManager::LoadDSiSDCard(); + DSiArgs args { + std::move(ndsargs), + std::move(arm9ibios), + std::move(arm7ibios), + std::move(*nand), + std::move(sdcard), + Config::DSiFullBIOSBoot, + }; + + args.GBAROM = nullptr; + + return std::make_unique(std::move(args)); + } + + return std::make_unique(std::move(ndsargs)); +} + +bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept +{ + // Let's get the cart we want to use; + // if we wnat to keep the cart, we'll eject it from the existing console first. + std::unique_ptr nextndscart; + if (std::holds_alternative(ndsargs)) + { // If we want to keep the existing cart (if any)... + nextndscart = NDS ? NDS->EjectCart() : nullptr; + ndsargs = {}; + } + else if (const auto ptr = std::get_if>(&ndsargs)) + { + nextndscart = std::move(*ptr); + ndsargs = {}; + } + + if (auto* cartsd = dynamic_cast(nextndscart.get())) + { + // LoadDLDISDCard will return nullopt if the SD card is disabled; + // SetSDCard will accept nullopt, which means no SD card + cartsd->SetSDCard(ROMManager::GetDLDISDCardArgs()); + } + + std::unique_ptr nextgbacart; + if (std::holds_alternative(gbaargs)) + { + nextgbacart = NDS ? NDS->EjectGBACart() : nullptr; + } + else if (const auto ptr = std::get_if>(&gbaargs)) + { + nextgbacart = std::move(*ptr); + gbaargs = {}; + } + + if (!NDS || NDS->ConsoleType != Config::ConsoleType) + { // If we're switching between DS and DSi mode, or there's no console... + // To ensure the destructor is called before a new one is created, + // as the presence of global signal handlers still complicates things a bit + NDS = nullptr; + NDS::Current = nullptr; + + NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart)); + + if (NDS == nullptr) + return false; + + NDS->Reset(); + NDS::Current = NDS.get(); + + return true; + } + + auto arm9bios = ROMManager::LoadARM9BIOS(); + if (!arm9bios) + return false; + + auto arm7bios = ROMManager::LoadARM7BIOS(); + if (!arm7bios) + return false; + + auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType); + if (!firmware) + return false; + + if (NDS->ConsoleType == 1) + { // If the console we're updating is a DSi... + DSi& dsi = static_cast(*NDS); + + auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); + if (!arm9ibios) + return false; + + auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); + if (!arm7ibios) + return false; + + auto nandimage = ROMManager::LoadNAND(*arm7ibios); + if (!nandimage) + return false; + + auto dsisdcard = ROMManager::LoadDSiSDCard(); + + dsi.SetFullBIOSBoot(Config::DSiFullBIOSBoot); + dsi.ARM7iBIOS = *arm7ibios; + dsi.ARM9iBIOS = *arm9ibios; + dsi.SetNAND(std::move(*nandimage)); + dsi.SetSDCard(std::move(dsisdcard)); + // We're moving the optional, not the card + // (inserting std::nullopt here is okay, it means no card) + + dsi.EjectGBACart(); + } + + if (NDS->ConsoleType == 0) + { + NDS->SetGBACart(std::move(nextgbacart)); + } + +#ifdef JIT_ENABLED + JITArgs jitargs { + static_cast(Config::JIT_MaxBlockSize), + Config::JIT_LiteralOptimisations, + Config::JIT_BranchOptimisations, + Config::JIT_FastMemory, + }; + NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); +#endif + NDS->SetARM7BIOS(*arm7bios); + NDS->SetARM9BIOS(*arm9bios); + NDS->SetFirmware(std::move(*firmware)); + NDS->SetNDSCart(std::move(nextndscart)); + NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); + NDS->SPU.SetDegrade10Bit(static_cast(Config::AudioBitDepth)); + + NDS::Current = NDS.get(); + + return true; +} + +void EmuThread::run() +{ + u32 mainScreenPos[3]; + Platform::FileHandle* file; + + UpdateConsole(nullptr, nullptr); + // No carts are inserted when melonDS first boots + + mainScreenPos[0] = 0; + mainScreenPos[1] = 0; + mainScreenPos[2] = 0; + autoScreenSizing = 0; + + videoSettingsDirty = false; + + if (mainWindow->hasOGL) + { + screenGL = static_cast(mainWindow->panel); + screenGL->initOpenGL(); + videoRenderer = Config::_3DRenderer; + } + else + { + screenGL = nullptr; + videoRenderer = 0; + } + + if (videoRenderer == 0) + { // If we're using the software renderer... + NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); + } + else + { + auto glrenderer = melonDS::GLRenderer::New(); + glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); + NDS->GPU.SetRenderer3D(std::move(glrenderer)); + } + + Input::Init(); + + u32 nframes = 0; + double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency(); + double lastTime = SDL_GetPerformanceCounter() * perfCountsSec; + double frameLimitError = 0.0; + double lastMeasureTime = lastTime; + + u32 winUpdateCount = 0, winUpdateFreq = 1; + u8 dsiVolumeLevel = 0x1F; + + file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read); + if (file) + { + RTC::StateData state; + Platform::FileRead(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + NDS->RTC.SetState(state); + } + + char melontitle[100]; + + while (EmuRunning != emuStatus_Exit) + { + Input::Process(); + + if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); + + if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause(); + if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset(); + if (Input::HotkeyPressed(HK_FrameStep)) emit windowEmuFrameStep(); + + if (Input::HotkeyPressed(HK_FullscreenToggle)) emit windowFullscreenToggle(); + + if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle(); + if (Input::HotkeyPressed(HK_SwapScreenEmphasis)) emit screenEmphasisToggle(); + + if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) + { + EmuStatus = emuStatus_Running; + if (EmuRunning == emuStatus_FrameStep) EmuRunning = emuStatus_Paused; + + if (Input::HotkeyPressed(HK_SolarSensorDecrease)) + { + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); + if (level != -1) + { + mainWindow->osdAddMessage(0, "Solar sensor level: %d", level); + } + } + if (Input::HotkeyPressed(HK_SolarSensorIncrease)) + { + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); + if (level != -1) + { + mainWindow->osdAddMessage(0, "Solar sensor level: %d", level); + } + } + + if (NDS->ConsoleType == 1) + { + DSi& dsi = static_cast(*NDS); + double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; + + // Handle power button + if (Input::HotkeyDown(HK_PowerButton)) + { + dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime); + } + else if (Input::HotkeyReleased(HK_PowerButton)) + { + dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime); + } + + // Handle volume buttons + if (Input::HotkeyDown(HK_VolumeUp)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); + } + else if (Input::HotkeyReleased(HK_VolumeUp)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); + } + + if (Input::HotkeyDown(HK_VolumeDown)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); + } + else if (Input::HotkeyReleased(HK_VolumeDown)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); + } + + dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime); + } + + // update render settings if needed + // HACK: + // once the fast forward hotkey is released, we need to update vsync + // to the old setting again + if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward)) + { + if (screenGL) + { + screenGL->setSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); + videoRenderer = Config::_3DRenderer; + } +#ifdef OGLRENDERER_ENABLED + else +#endif + { + videoRenderer = 0; + } + + videoRenderer = screenGL ? Config::_3DRenderer : 0; + + videoSettingsDirty = false; + + if (videoRenderer == 0) + { // If we're using the software renderer... + NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); + } + else + { + auto glrenderer = melonDS::GLRenderer::New(); + glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); + NDS->GPU.SetRenderer3D(std::move(glrenderer)); + } + } + + // process input and hotkeys + NDS->SetKeyMask(Input::InputMask); + + if (Input::HotkeyPressed(HK_Lid)) + { + bool lid = !NDS->IsLidClosed(); + NDS->SetLidClosed(lid); + mainWindow->osdAddMessage(0, lid ? "Lid closed" : "Lid opened"); + } + + // microphone input + AudioInOut::MicProcess(*NDS); + + // auto screen layout + if (Config::ScreenSizing == Frontend::screenSizing_Auto) + { + mainScreenPos[2] = mainScreenPos[1]; + mainScreenPos[1] = mainScreenPos[0]; + mainScreenPos[0] = NDS->PowerControl9 >> 15; + + int guess; + if (mainScreenPos[0] == mainScreenPos[2] && + mainScreenPos[0] != mainScreenPos[1]) + { + // constant flickering, likely displaying 3D on both screens + // TODO: when both screens are used for 2D only...??? + guess = Frontend::screenSizing_Even; + } + else + { + if (mainScreenPos[0] == 1) + guess = Frontend::screenSizing_EmphTop; + else + guess = Frontend::screenSizing_EmphBot; + } + + if (guess != autoScreenSizing) + { + autoScreenSizing = guess; + emit screenLayoutChange(); + } + } + + + // emulate + u32 nlines = NDS->RunFrame(); + + if (ROMManager::NDSSave) + ROMManager::NDSSave->CheckFlush(); + + if (ROMManager::GBASave) + ROMManager::GBASave->CheckFlush(); + + if (ROMManager::FirmwareSave) + ROMManager::FirmwareSave->CheckFlush(); + + if (!screenGL) + { + FrontBufferLock.lock(); + FrontBuffer = NDS->GPU.FrontBuffer; + FrontBufferLock.unlock(); + } + else + { + FrontBuffer = NDS->GPU.FrontBuffer; + screenGL->drawScreenGL(); + } + +#ifdef MELONCAP + MelonCap::Update(); +#endif // MELONCAP + + if (EmuRunning == emuStatus_Exit) break; + + winUpdateCount++; + if (winUpdateCount >= winUpdateFreq && !screenGL) + { + emit windowUpdate(); + winUpdateCount = 0; + } + + bool fastforward = Input::HotkeyDown(HK_FastForward); + + if (fastforward && screenGL && Config::ScreenVSync) + { + screenGL->setSwapInterval(0); + } + + if (Config::DSiVolumeSync && NDS->ConsoleType == 1) + { + DSi& dsi = static_cast(*NDS); + u8 volumeLevel = dsi.I2C.GetBPTWL()->GetVolumeLevel(); + if (volumeLevel != dsiVolumeLevel) + { + dsiVolumeLevel = volumeLevel; + emit syncVolumeLevel(); + } + + Config::AudioVolume = volumeLevel * (256.0 / 31.0); + } + + if (Config::AudioSync && !fastforward) + AudioInOut::AudioSync(*this->NDS); + + double frametimeStep = nlines / (60.0 * 263.0); + + { + bool limitfps = Config::LimitFPS && !fastforward; + + double practicalFramelimit = limitfps ? frametimeStep : 1.0 / Config::MaxFPS; + + double curtime = SDL_GetPerformanceCounter() * perfCountsSec; + + frameLimitError += practicalFramelimit - (curtime - lastTime); + if (frameLimitError < -practicalFramelimit) + frameLimitError = -practicalFramelimit; + if (frameLimitError > practicalFramelimit) + frameLimitError = practicalFramelimit; + + if (round(frameLimitError * 1000.0) > 0.0) + { + SDL_Delay(round(frameLimitError * 1000.0)); + double timeBeforeSleep = curtime; + curtime = SDL_GetPerformanceCounter() * perfCountsSec; + frameLimitError -= curtime - timeBeforeSleep; + } + + lastTime = curtime; + } + + nframes++; + if (nframes >= 30) + { + double time = SDL_GetPerformanceCounter() * perfCountsSec; + double dt = time - lastMeasureTime; + lastMeasureTime = time; + + u32 fps = round(nframes / dt); + nframes = 0; + + float fpstarget = 1.0/frametimeStep; + + winUpdateFreq = fps / (u32)round(fpstarget); + if (winUpdateFreq < 1) + winUpdateFreq = 1; + + int inst = Platform::InstanceID(); + if (inst == 0) + sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); + else + sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); + changeWindowTitle(melontitle); + } + } + else + { + // paused + nframes = 0; + lastTime = SDL_GetPerformanceCounter() * perfCountsSec; + lastMeasureTime = lastTime; + + emit windowUpdate(); + + EmuStatus = EmuRunning; + + int inst = Platform::InstanceID(); + if (inst == 0) + sprintf(melontitle, "melonDS " MELONDS_VERSION); + else + sprintf(melontitle, "melonDS (%d)", inst+1); + changeWindowTitle(melontitle); + + SDL_Delay(75); + + if (screenGL) + screenGL->drawScreenGL(); + + ContextRequestKind contextRequest = ContextRequest; + if (contextRequest == contextRequest_InitGL) + { + screenGL = static_cast(mainWindow->panel); + screenGL->initOpenGL(); + ContextRequest = contextRequest_None; + } + else if (contextRequest == contextRequest_DeInitGL) + { + screenGL->deinitOpenGL(); + screenGL = nullptr; + ContextRequest = contextRequest_None; + } + } + } + + file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write); + if (file) + { + RTC::StateData state; + NDS->RTC.GetState(state); + Platform::FileWrite(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + } + + EmuStatus = emuStatus_Exit; + + NDS::Current = nullptr; + // nds is out of scope, so unique_ptr cleans it up for us +} + +void EmuThread::changeWindowTitle(char* title) +{ + emit windowTitleChange(QString(title)); +} + +void EmuThread::emuRun() +{ + EmuRunning = emuStatus_Running; + EmuPauseStack = EmuPauseStackRunning; + RunningSomething = true; + + // checkme + emit windowEmuStart(); + AudioInOut::Enable(); +} + +void EmuThread::initContext() +{ + ContextRequest = contextRequest_InitGL; + while (ContextRequest != contextRequest_None); +} + +void EmuThread::deinitContext() +{ + ContextRequest = contextRequest_DeInitGL; + while (ContextRequest != contextRequest_None); +} + +void EmuThread::emuPause() +{ + EmuPauseStack++; + if (EmuPauseStack > EmuPauseStackPauseThreshold) return; + + PrevEmuStatus = EmuRunning; + EmuRunning = emuStatus_Paused; + while (EmuStatus != emuStatus_Paused); + + AudioInOut::Disable(); +} + +void EmuThread::emuUnpause() +{ + if (EmuPauseStack < EmuPauseStackPauseThreshold) return; + + EmuPauseStack--; + if (EmuPauseStack >= EmuPauseStackPauseThreshold) return; + + EmuRunning = PrevEmuStatus; + + AudioInOut::Enable(); +} + +void EmuThread::emuStop() +{ + EmuRunning = emuStatus_Exit; + EmuPauseStack = EmuPauseStackRunning; + + AudioInOut::Disable(); +} + +void EmuThread::emuFrameStep() +{ + if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause(); + EmuRunning = emuStatus_FrameStep; +} + +bool EmuThread::emuIsRunning() +{ + return EmuRunning == emuStatus_Running; +} + +bool EmuThread::emuIsActive() +{ + return (RunningSomething == 1); +} diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h new file mode 100644 index 00000000..4950ebbf --- /dev/null +++ b/src/frontend/qt_sdl/EmuThread.h @@ -0,0 +1,134 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef EMUTHREAD_H +#define EMUTHREAD_H + +#include +#include + +#include +#include +#include + +#include "NDSCart.h" +#include "GBACart.h" + +using Keep = std::monostate; +using UpdateConsoleNDSArgs = std::variant>; +using UpdateConsoleGBAArgs = std::variant>; +namespace melonDS +{ +class NDS; +} + +class ScreenPanelGL; + +class EmuThread : public QThread +{ + Q_OBJECT + void run() override; + +public: + explicit EmuThread(QObject* parent = nullptr); + + void changeWindowTitle(char* title); + + // to be called from the UI thread + void emuRun(); + void emuPause(); + void emuUnpause(); + void emuStop(); + void emuFrameStep(); + + bool emuIsRunning(); + bool emuIsActive(); + + void initContext(); + void deinitContext(); + + int FrontBuffer = 0; + QMutex FrontBufferLock; + + /// Applies the config in args. + /// Creates a new NDS console if needed, + /// modifies the existing one if possible. + /// @return \c true if the console was updated. + /// If this returns \c false, then the existing NDS console is not modified. + bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept; + std::unique_ptr NDS; // TODO: Proper encapsulation and synchronization +signals: + void windowUpdate(); + void windowTitleChange(QString title); + + void windowEmuStart(); + void windowEmuStop(); + void windowEmuPause(); + void windowEmuReset(); + void windowEmuFrameStep(); + + void windowLimitFPSChange(); + + void screenLayoutChange(); + + void windowFullscreenToggle(); + + void swapScreensToggle(); + void screenEmphasisToggle(); + + void syncVolumeLevel(); + +private: + std::unique_ptr CreateConsole( + std::unique_ptr&& ndscart, + std::unique_ptr&& gbacart + ) noexcept; + + enum EmuStatusKind + { + emuStatus_Exit, + emuStatus_Running, + emuStatus_Paused, + emuStatus_FrameStep, + }; + std::atomic EmuStatus; + + EmuStatusKind PrevEmuStatus; + EmuStatusKind EmuRunning; + + constexpr static int EmuPauseStackRunning = 0; + constexpr static int EmuPauseStackPauseThreshold = 1; + int EmuPauseStack; + + enum ContextRequestKind + { + contextRequest_None = 0, + contextRequest_InitGL, + contextRequest_DeInitGL + }; + std::atomic ContextRequest = contextRequest_None; + + ScreenPanelGL* screenGL; + + int autoScreenSizing; + + int videoRenderer; + bool videoSettingsDirty; +}; + +#endif // EMUTHREAD_H diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp index ffca5676..1ec2e8c4 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,6 +24,8 @@ #include "FirmwareSettingsDialog.h" #include "ui_FirmwareSettingsDialog.h" +using namespace melonDS::Platform; +namespace Platform = melonDS::Platform; FirmwareSettingsDialog* FirmwareSettingsDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.h b/src/frontend/qt_sdl/FirmwareSettingsDialog.h index b3695e2f..d22ce3a2 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.h +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index c1ef87c9..7ebd7e2a 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,6 +22,7 @@ #include "Input.h" #include "Config.h" +using namespace melonDS; namespace Input { @@ -127,6 +128,11 @@ void KeyRelease(QKeyEvent* event) KeyHotkeyMask &= ~(1< + #include "types.h" namespace Input { +using namespace melonDS; extern int JoystickID; extern SDL_Joystick* Joystick; @@ -37,6 +40,7 @@ void CloseJoystick(); void KeyPress(QKeyEvent* event); void KeyRelease(QKeyEvent* event); +void KeyReleaseAll(); void Process(); diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp index b0672a04..02a76bb7 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -32,6 +32,7 @@ #include "ui_InputConfigDialog.h" +using namespace melonDS; InputConfigDialog* InputConfigDialog::currentDlg = nullptr; const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3}; diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 53ea8762..8d4f882a 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/MapButton.h b/src/frontend/qt_sdl/InputConfig/MapButton.h index f90fa3f1..5d4fb3eb 100644 --- a/src/frontend/qt_sdl/InputConfig/MapButton.h +++ b/src/frontend/qt_sdl/InputConfig/MapButton.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -52,7 +52,7 @@ protected: { if (!isChecked()) return QPushButton::keyPressEvent(event); - Platform::Log(Platform::Debug, "KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), (int)event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode()); + Log(melonDS::Platform::Debug, "KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), (int)event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode()); int key = event->key(); int mod = event->modifiers(); diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index 7c5eae61..851e7abf 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -16,15 +16,16 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include #include "InterfaceSettingsDialog.h" #include "ui_InterfaceSettingsDialog.h" #include "types.h" #include "Platform.h" #include "Config.h" +#include "main.h" InterfaceSettingsDialog* InterfaceSettingsDialog::currentDlg = nullptr; - InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InterfaceSettingsDialog) { ui->setupUi(this); @@ -34,6 +35,19 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare ui->spinMouseHideSeconds->setEnabled(Config::MouseHide != 0); ui->spinMouseHideSeconds->setValue(Config::MouseHideSeconds); ui->cbPauseLostFocus->setChecked(Config::PauseLostFocus != 0); + ui->spinMaxFPS->setValue(Config::MaxFPS); + + const QList themeKeys = QStyleFactory::keys(); + const QString currentTheme = qApp->style()->objectName(); + + ui->cbxUITheme->addItem("System default", ""); + + for (int i = 0; i < themeKeys.length(); i++) + { + ui->cbxUITheme->addItem(themeKeys[i], themeKeys[i]); + if (!Config::UITheme.empty() && themeKeys[i].compare(currentTheme, Qt::CaseInsensitive) == 0) + ui->cbxUITheme->setCurrentIndex(i + 1); + } } InterfaceSettingsDialog::~InterfaceSettingsDialog() @@ -60,9 +74,18 @@ void InterfaceSettingsDialog::done(int r) Config::MouseHide = ui->cbMouseHide->isChecked() ? 1:0; Config::MouseHideSeconds = ui->spinMouseHideSeconds->value(); Config::PauseLostFocus = ui->cbPauseLostFocus->isChecked() ? 1:0; + Config::MaxFPS = ui->spinMaxFPS->value(); + + QString themeName = ui->cbxUITheme->currentData().toString(); + Config::UITheme = themeName.toStdString(); Config::Save(); + if (!Config::UITheme.empty()) + qApp->setStyle(themeName); + else + qApp->setStyle(*systemThemeName); + emit updateMouseTimer(); } diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.h b/src/frontend/qt_sdl/InterfaceSettingsDialog.h index 114aa047..5a23b6ea 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.h +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui index 8ee9feda..21d8434e 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui @@ -6,8 +6,8 @@ 0 0 - 262 - 113 + 337 + 275 @@ -19,32 +19,113 @@ Interface settings - melonDS - - - - - Hide after + + + + + User interface + + + + + + + Theme + + + cbxUITheme + + + + + + + + + + + + Hide mouse after inactivity + + + + + + + 18 + + + + + After + + + spinMouseHideSeconds + + + + + + + + + + seconds + + + spinMouseHideSeconds + + + + + + + + + Pause emulation when window is not in focus + + + + - - - - Pause emulation when window is not in focus + + + + Framerate + + + + + Fast-forward limit + + + spinMaxFPS + + + + + + + FPS + + + 60 + + + 1000 + + + 1000 + + + + - - - - Hide mouse after inactivity - - - - - - - + Qt::Horizontal @@ -54,20 +135,8 @@ - - - - seconds of inactivity - - - - - cbMouseHide - spinMouseHideSeconds - cbPauseLostFocus - diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp index f5bf4366..4a1ebc35 100644 --- a/src/frontend/qt_sdl/LAN_PCap.cpp +++ b/src/frontend/qt_sdl/LAN_PCap.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -41,6 +41,7 @@ #endif #endif +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/frontend/qt_sdl/LAN_PCap.h b/src/frontend/qt_sdl/LAN_PCap.h index 2f4663f0..2e03d66a 100644 --- a/src/frontend/qt_sdl/LAN_PCap.h +++ b/src/frontend/qt_sdl/LAN_PCap.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,6 +24,7 @@ namespace LAN_PCap { +using namespace melonDS; struct AdapterData { char DeviceName[128]; diff --git a/src/frontend/qt_sdl/LAN_Socket.cpp b/src/frontend/qt_sdl/LAN_Socket.cpp index 6753bb87..e938af80 100644 --- a/src/frontend/qt_sdl/LAN_Socket.cpp +++ b/src/frontend/qt_sdl/LAN_Socket.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -37,6 +37,7 @@ #include #endif +using namespace melonDS; namespace LAN_Socket { diff --git a/src/frontend/qt_sdl/LAN_Socket.h b/src/frontend/qt_sdl/LAN_Socket.h index f2c7ed44..043e1330 100644 --- a/src/frontend/qt_sdl/LAN_Socket.h +++ b/src/frontend/qt_sdl/LAN_Socket.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -23,6 +23,7 @@ namespace LAN_Socket { +using namespace melonDS; // diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index 870ea8b8..7ea98686 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -38,6 +38,9 @@ #include "LocalMP.h" #include "Platform.h" +using namespace melonDS; +using namespace melonDS::Platform; + using Platform::Log; using Platform::LogLevel; @@ -127,7 +130,7 @@ bool SemInit(int num) char semname[64]; sprintf(semname, "Local\\melonNIFI_Sem%02d", num); - HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname); + HANDLE sem = CreateSemaphoreA(nullptr, 0, 64, semname); SemPool[num] = sem; SemInited[num] = true; return sem != INVALID_HANDLE_VALUE; @@ -245,7 +248,9 @@ bool Init() Log(LogLevel::Info, "MP sharedmem doesn't exist. creating\n"); if (!MPQueue->create(kQueueSize)) { - Log(LogLevel::Error, "MP sharedmem create failed :(\n"); + Log(LogLevel::Error, "MP sharedmem create failed :( (%d)\n", MPQueue->error()); + delete MPQueue; + MPQueue = nullptr; return false; } @@ -300,10 +305,13 @@ void DeInit() if (MPQueue) { MPQueue->lock(); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - header->ConnectedBitmask &= ~(1 << InstanceID); - header->InstanceBitmask &= ~(1 << InstanceID); - header->NumInstances--; + if (MPQueue->data() != nullptr) + { + MPQueueHeader *header = (MPQueueHeader *) MPQueue->data(); + header->ConnectedBitmask &= ~(1 << InstanceID); + header->InstanceBitmask &= ~(1 << InstanceID); + header->NumInstances--; + } MPQueue->unlock(); SemPoolDeinit(); @@ -311,8 +319,8 @@ void DeInit() MPQueue->detach(); } - MPQueue = nullptr; delete MPQueue; + MPQueue = nullptr; } void SetRecvTimeout(int timeout) @@ -322,6 +330,7 @@ void SetRecvTimeout(int timeout) void Begin() { + if (!MPQueue) return; MPQueue->lock(); MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); PacketReadOffset = header->PacketWriteOffset; @@ -334,6 +343,7 @@ void Begin() void End() { + if (!MPQueue) return; MPQueue->lock(); MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); //SemReset(InstanceID); @@ -415,6 +425,7 @@ void FIFOWrite(int fifo, void* buf, int len) int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) { + if (!MPQueue) return 0; MPQueue->lock(); u8* data = (u8*)MPQueue->data(); MPQueueHeader* header = (MPQueueHeader*)&data[0]; @@ -470,6 +481,7 @@ int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) int RecvPacketGeneric(u8* packet, bool block, u64* timestamp) { + if (!MPQueue) return 0; for (;;) { if (!SemWait(InstanceID, block ? RecvTimeout : 0)) @@ -546,6 +558,8 @@ int SendAck(u8* packet, int len, u64 timestamp) int RecvHostPacket(u8* packet, u64* timestamp) { + if (!MPQueue) return -1; + if (LastHostID != -1) { // check if the host is still connected @@ -565,6 +579,8 @@ int RecvHostPacket(u8* packet, u64* timestamp) u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask) { + if (!MPQueue) return 0; + u16 ret = 0; u16 myinstmask = (1 << InstanceID); u16 curinstmask; diff --git a/src/frontend/qt_sdl/LocalMP.h b/src/frontend/qt_sdl/LocalMP.h index 51dfcb93..e7b4188a 100644 --- a/src/frontend/qt_sdl/LocalMP.h +++ b/src/frontend/qt_sdl/LocalMP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -24,6 +24,7 @@ namespace LocalMP { +using namespace melonDS; bool Init(); void DeInit(); diff --git a/src/frontend/qt_sdl/MPSettingsDialog.cpp b/src/frontend/qt_sdl/MPSettingsDialog.cpp index e3114220..bd64dfa2 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.cpp +++ b/src/frontend/qt_sdl/MPSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/MPSettingsDialog.h b/src/frontend/qt_sdl/MPSettingsDialog.h index fe917e89..837ac8db 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.h +++ b/src/frontend/qt_sdl/MPSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp deleted file mode 100644 index 6842d5f0..00000000 --- a/src/frontend/qt_sdl/OSD.cpp +++ /dev/null @@ -1,472 +0,0 @@ -/* - Copyright 2016-2022 melonDS team - - This file is part of melonDS. - - melonDS is free software: you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation, either version 3 of the License, or (at your option) - any later version. - - melonDS is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with melonDS. If not, see http://www.gnu.org/licenses/. -*/ - -#include -#include -#include -#include -#include "../types.h" - -#include "main.h" -#include "OpenGLSupport.h" -#include - -#include "OSD.h" -#include "OSD_shaders.h" -#include "font.h" - -#include "Config.h" - -extern MainWindow* mainWindow; - -namespace OSD -{ - -const u32 kOSDMargin = 6; - -struct Item -{ - Uint32 Timestamp; - char Text[256]; - u32 Color; - - u32 Width, Height; - u32* Bitmap; - - bool NativeBitmapLoaded; - QImage NativeBitmap; - - bool GLTextureLoaded; - GLuint GLTexture; -}; - -std::deque ItemQueue; - -GLuint Shader; -GLint uScreenSize, uOSDPos, uOSDSize; -GLfloat uScaleFactor; -GLuint OSDVertexArray; -GLuint OSDVertexBuffer; - -QMutex Rendering; - - -bool Init(bool openGL) -{ - if (openGL) - { - OpenGL::CompileVertexFragmentProgram(Shader, - kScreenVS_OSD, kScreenFS_OSD, - "OSDShader", - {{"vPosition", 0}}, - {{"oColor", 0}}); - - glUseProgram(Shader); - glUniform1i(glGetUniformLocation(Shader, "OSDTex"), 0); - - uScreenSize = glGetUniformLocation(Shader, "uScreenSize"); - uOSDPos = glGetUniformLocation(Shader, "uOSDPos"); - uOSDSize = glGetUniformLocation(Shader, "uOSDSize"); - uScaleFactor = glGetUniformLocation(Shader, "uScaleFactor"); - - float vertices[6*2] = - { - 0, 0, - 1, 1, - 1, 0, - 0, 0, - 0, 1, - 1, 1 - }; - - glGenBuffers(1, &OSDVertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glGenVertexArrays(1, &OSDVertexArray); - glBindVertexArray(OSDVertexArray); - glEnableVertexAttribArray(0); // position - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); - } - - return true; -} - -void DeInit() -{ - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); - if (item.Bitmap) delete[] item.Bitmap; - - it = ItemQueue.erase(it); - } -} - - -int FindBreakPoint(const char* text, int i) -{ - // i = character that went out of bounds - - for (int j = i; j >= 0; j--) - { - if (text[j] == ' ') - return j; - } - - return i; -} - -void LayoutText(const char* text, u32* width, u32* height, int* breaks) -{ - u32 w = 0; - u32 h = 14; - u32 totalw = 0; - u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2); - int lastbreak = -1; - int numbrk = 0; - u16* ptr; - - memset(breaks, 0, sizeof(int)*64); - - for (int i = 0; text[i] != '\0'; ) - { - int glyphsize; - if (text[i] == ' ') - { - glyphsize = 6; - } - else - { - u32 ch = text[i]; - if (ch < 0x10 || ch > 0x7E) ch = 0x7F; - - ptr = &font[(ch-0x10) << 4]; - glyphsize = ptr[0]; - if (!glyphsize) glyphsize = 6; - else glyphsize += 2; // space around the character - } - - w += glyphsize; - if (w > maxw) - { - // wrap shit as needed - if (text[i] == ' ') - { - if (numbrk >= 64) break; - breaks[numbrk++] = i; - i++; - } - else - { - int brk = FindBreakPoint(text, i); - if (brk != lastbreak) i = brk; - - if (numbrk >= 64) break; - breaks[numbrk++] = i; - - lastbreak = brk; - } - - w = 0; - h += 14; - } - else - i++; - - if (w > totalw) totalw = w; - } - - *width = totalw; - *height = h; -} - -u32 RainbowColor(u32 inc) -{ - // inspired from Acmlmboard - - if (inc < 100) return 0xFFFF9B9B + (inc << 8); - else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16); - else if (inc < 300) return 0xFF9BFF9B + (inc-200); - else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8); - else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16); - else return 0xFFFF9BFF - (inc-500); -} - -void RenderText(u32 color, const char* text, Item* item) -{ - u32 w, h; - int breaks[64]; - - bool rainbow = (color == 0); - u32 rainbowinc = ((text[0] * 17) + (SDL_GetTicks() * 13)) % 600; - - color |= 0xFF000000; - const u32 shadow = 0xE0000000; - - LayoutText(text, &w, &h, breaks); - - item->Width = w; - item->Height = h; - item->Bitmap = new u32[w*h]; - memset(item->Bitmap, 0, w*h*sizeof(u32)); - - u32 x = 0, y = 1; - u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2); - int curline = 0; - u16* ptr; - - for (int i = 0; text[i] != '\0'; ) - { - int glyphsize; - if (text[i] == ' ') - { - x += 6; - } - else - { - u32 ch = text[i]; - if (ch < 0x10 || ch > 0x7E) ch = 0x7F; - - ptr = &font[(ch-0x10) << 4]; - int glyphsize = ptr[0]; - if (!glyphsize) x += 6; - else - { - x++; - - if (rainbow) - { - color = RainbowColor(rainbowinc); - rainbowinc = (rainbowinc + 30) % 600; - } - - // draw character - for (int cy = 0; cy < 12; cy++) - { - u16 val = ptr[4+cy]; - - for (int cx = 0; cx < glyphsize; cx++) - { - if (val & (1<Bitmap[((y+cy) * w) + x+cx] = color; - } - } - - x += glyphsize; - x++; - } - } - - i++; - if (breaks[curline] && i >= breaks[curline]) - { - i = breaks[curline++]; - if (text[i] == ' ') i++; - - x = 0; - y += 14; - } - } - - // shadow - for (y = 0; y < h; y++) - { - for (x = 0; x < w; x++) - { - u32 val; - - val = item->Bitmap[(y * w) + x]; - if ((val >> 24) == 0xFF) continue; - - if (x > 0) val = item->Bitmap[(y * w) + x-1]; - if (x < w-1) val |= item->Bitmap[(y * w) + x+1]; - if (y > 0) - { - if (x > 0) val |= item->Bitmap[((y-1) * w) + x-1]; - val |= item->Bitmap[((y-1) * w) + x]; - if (x < w-1) val |= item->Bitmap[((y-1) * w) + x+1]; - } - if (y < h-1) - { - if (x > 0) val |= item->Bitmap[((y+1) * w) + x-1]; - val |= item->Bitmap[((y+1) * w) + x]; - if (x < w-1) val |= item->Bitmap[((y+1) * w) + x+1]; - } - - if ((val >> 24) == 0xFF) - item->Bitmap[(y * w) + x] = shadow; - } - } -} - - -void AddMessage(u32 color, const char* text) -{ - if (!Config::ShowOSD) return; - - Rendering.lock(); - - Item item; - - item.Timestamp = SDL_GetTicks(); - strncpy(item.Text, text, 255); item.Text[255] = '\0'; - item.Color = color; - item.Bitmap = nullptr; - - item.NativeBitmapLoaded = false; - item.GLTextureLoaded = false; - - ItemQueue.push_back(item); - - Rendering.unlock(); -} - -void Update() -{ - if (!Config::ShowOSD) - { - Rendering.lock(); - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); - if (item.Bitmap) delete[] item.Bitmap; - - it = ItemQueue.erase(it); - } - Rendering.unlock(); - return; - } - - Rendering.lock(); - - Uint32 tick_now = SDL_GetTicks(); - Uint32 tick_min = tick_now - 2500; - - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (item.Timestamp < tick_min) - { - if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); - if (item.Bitmap) delete[] item.Bitmap; - - it = ItemQueue.erase(it); - continue; - } - - if (!item.Bitmap) - { - RenderText(item.Color, item.Text, &item); - } - - it++; - } - - Rendering.unlock(); -} - -void DrawNative(QPainter& painter) -{ - if (!Config::ShowOSD) return; - - Rendering.lock(); - - u32 y = kOSDMargin; - - painter.resetTransform(); - - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (!item.NativeBitmapLoaded) - { - item.NativeBitmap = QImage((const uchar*)item.Bitmap, item.Width, item.Height, QImage::Format_ARGB32_Premultiplied); - item.NativeBitmapLoaded = true; - } - - painter.drawImage(kOSDMargin, y, item.NativeBitmap); - - y += item.Height; - it++; - } - - Rendering.unlock(); -} - -void DrawGL(float w, float h) -{ - if (!Config::ShowOSD) return; - if (!mainWindow || !mainWindow->panel) return; - - Rendering.lock(); - - u32 y = kOSDMargin; - - glUseProgram(Shader); - - glUniform2f(uScreenSize, w, h); - glUniform1f(uScaleFactor, mainWindow->devicePixelRatioF()); - - glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); - glBindVertexArray(OSDVertexArray); - - glActiveTexture(GL_TEXTURE0); - - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (!item.GLTextureLoaded) - { - glGenTextures(1, &item.GLTexture); - glBindTexture(GL_TEXTURE_2D, item.GLTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap); - - item.GLTextureLoaded = true; - } - - glBindTexture(GL_TEXTURE_2D, item.GLTexture); - glUniform2i(uOSDPos, kOSDMargin, y); - glUniform2i(uOSDSize, item.Width, item.Height); - glDrawArrays(GL_TRIANGLES, 0, 2*3); - - y += item.Height; - it++; - } - - glDisable(GL_BLEND); - glUseProgram(0); - - Rendering.unlock(); -} - -} diff --git a/src/frontend/qt_sdl/OSD_shaders.h b/src/frontend/qt_sdl/OSD_shaders.h index e224fd13..1324fd9d 100644 --- a/src/frontend/qt_sdl/OSD_shaders.h +++ b/src/frontend/qt_sdl/OSD_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index 286032e9..71342087 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,7 @@ #include #include #include +#include #include "types.h" #include "Config.h" @@ -27,6 +28,8 @@ #include "PathSettingsDialog.h" #include "ui_PathSettingsDialog.h" +using namespace melonDS::Platform; +namespace Platform = melonDS::Platform; PathSettingsDialog* PathSettingsDialog::currentDlg = nullptr; @@ -35,6 +38,7 @@ extern bool RunningSomething; bool PathSettingsDialog::needsReset = false; +constexpr char errordialog[] = "melonDS cannot write to that directory."; PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PathSettingsDialog) { @@ -99,6 +103,12 @@ void PathSettingsDialog::on_btnSaveFileBrowse_clicked() QString::fromStdString(EmuDirectory)); if (dir.isEmpty()) return; + + if (!QTemporaryFile(dir).open()) + { + QMessageBox::critical(this, "melonDS", errordialog); + return; + } ui->txtSaveFilePath->setText(dir); } @@ -110,6 +120,12 @@ void PathSettingsDialog::on_btnSavestateBrowse_clicked() QString::fromStdString(EmuDirectory)); if (dir.isEmpty()) return; + + if (!QTemporaryFile(dir).open()) + { + QMessageBox::critical(this, "melonDS", errordialog); + return; + } ui->txtSavestatePath->setText(dir); } @@ -121,6 +137,12 @@ void PathSettingsDialog::on_btnCheatFileBrowse_clicked() QString::fromStdString(EmuDirectory)); if (dir.isEmpty()) return; + + if (!QTemporaryFile(dir).open()) + { + QMessageBox::critical(this, "melonDS", errordialog); + return; + } ui->txtCheatFilePath->setText(dir); } diff --git a/src/frontend/qt_sdl/PathSettingsDialog.h b/src/frontend/qt_sdl/PathSettingsDialog.h index ef4fd2d0..dd64d46c 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.h +++ b/src/frontend/qt_sdl/PathSettingsDialog.h @@ -1,6 +1,6 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 0574d5d0..b3230a41 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,14 +21,17 @@ #include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include #include "Platform.h" @@ -38,7 +41,6 @@ #include "LAN_Socket.h" #include "LAN_PCap.h" #include "LocalMP.h" -#include "OSD.h" #include "SPI_Firmware.h" #ifdef __WIN32__ @@ -52,10 +54,50 @@ extern CameraManager* camManager[2]; void emuStop(); +// TEMP +//#include "main.h" +//extern MainWindow* mainWindow; -namespace Platform + +namespace melonDS::Platform { +void PathInit(int argc, char** argv) +{ + // First, check for the portable directory next to the executable. + QString appdirpath = QCoreApplication::applicationDirPath(); + QString portablepath = appdirpath + QDir::separator() + "portable"; + +#if defined(__APPLE__) + // On Apple platforms we may need to navigate outside an app bundle. + // The executable directory would be "melonDS.app/Contents/MacOS", so we need to go a total of three steps up. + QDir bundledir(appdirpath); + if (bundledir.cd("..") && bundledir.cd("..") && bundledir.dirName().endsWith(".app") && bundledir.cd("..")) + { + portablepath = bundledir.absolutePath() + QDir::separator() + "portable"; + } +#endif + + QDir portabledir(portablepath); + if (portabledir.exists()) + { + EmuDirectory = portabledir.absolutePath().toStdString(); + } + else + { + // If no overrides are specified, use the default path. +#if defined(__WIN32__) && defined(WIN32_PORTABLE) + EmuDirectory = appdirpath.toStdString(); +#else + QString confdir; + QDir config(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)); + config.mkdir("melonDS"); + confdir = config.absolutePath() + QDir::separator() + "melonDS"; + EmuDirectory = confdir.toStdString(); +#endif + } +} + QSharedMemory* IPCBuffer = nullptr; int IPCInstanceID; @@ -65,12 +107,25 @@ void IPCInit() IPCBuffer = new QSharedMemory("melonIPC"); +#if !defined(Q_OS_WINDOWS) + // QSharedMemory instances can be left over from crashed processes on UNIX platforms. + // To prevent melonDS thinking there's another instance, we attach and then immediately detach from the + // shared memory. If no other process was actually using it, it'll be destroyed and we'll have a clean + // shared memory buffer after creating it again below. + if (IPCBuffer->attach()) + { + IPCBuffer->detach(); + delete IPCBuffer; + IPCBuffer = new QSharedMemory("melonIPC"); + } +#endif + if (!IPCBuffer->attach()) { Log(LogLevel::Info, "IPC sharedmem doesn't exist. creating\n"); if (!IPCBuffer->create(1024)) { - Log(LogLevel::Error, "IPC sharedmem create failed :(\n"); + Log(LogLevel::Error, "IPC sharedmem create failed: %s\n", IPCBuffer->errorString().toStdString().c_str()); delete IPCBuffer; IPCBuffer = nullptr; return; @@ -116,38 +171,7 @@ void IPCDeInit() void Init(int argc, char** argv) { -#if defined(__WIN32__) || defined(PORTABLE) - if (argc > 0 && strlen(argv[0]) > 0) - { - int len = strlen(argv[0]); - while (len > 0) - { - if (argv[0][len] == '/') break; - if (argv[0][len] == '\\') break; - len--; - } - if (len > 0) - { - std::string emudir = argv[0]; - EmuDirectory = emudir.substr(0, len); - } - else - { - EmuDirectory = "."; - } - } - else - { - EmuDirectory = "."; - } -#else - QString confdir; - QDir config(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)); - config.mkdir("melonDS"); - confdir = config.absolutePath() + "/melonDS/"; - EmuDirectory = confdir.toStdString(); -#endif - + PathInit(argc, argv); IPCInit(); } @@ -163,14 +187,14 @@ void SignalStop(StopReason reason) { case StopReason::GBAModeNotSupported: Log(LogLevel::Error, "!! GBA MODE NOT SUPPORTED\n"); - OSD::AddMessage(0xFFA0A0, "GBA mode not supported."); + //mainWindow->osdAddMessage(0xFFA0A0, "GBA mode not supported."); break; case StopReason::BadExceptionRegion: - OSD::AddMessage(0xFFA0A0, "Internal error."); + //mainWindow->osdAddMessage(0xFFA0A0, "Internal error."); break; case StopReason::PowerOff: case StopReason::External: - OSD::AddMessage(0xFFC040, "Shutdown"); + //mainWindow->osdAddMessage(0xFFC040, "Shutdown"); default: break; } @@ -192,119 +216,33 @@ std::string InstanceFileSuffix() return suffix; } - -int GetConfigInt(ConfigEntry entry) +static QIODevice::OpenMode GetQMode(FileMode mode) { - const int imgsizes[] = {0, 256, 512, 1024, 2048, 4096}; + QIODevice::OpenMode qmode = QIODevice::OpenModeFlag::NotOpen; + if (mode & FileMode::Read) + qmode |= QIODevice::OpenModeFlag::ReadOnly; + if (mode & FileMode::Write) + qmode |= QIODevice::OpenModeFlag::WriteOnly; + if (mode & FileMode::Append) + qmode |= QIODevice::OpenModeFlag::Append; - switch (entry) - { -#ifdef JIT_ENABLED - case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize; -#endif + if ((mode & FileMode::Write) && !(mode & FileMode::Preserve)) + qmode |= QIODevice::OpenModeFlag::Truncate; - case DLDI_ImageSize: return imgsizes[Config::DLDISize]; + if (mode & FileMode::NoCreate) + qmode |= QIODevice::OpenModeFlag::ExistingOnly; - case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize]; + if (mode & FileMode::Text) + qmode |= QIODevice::OpenModeFlag::Text; - case Firm_Language: return Config::FirmwareLanguage; - case Firm_BirthdayMonth: return Config::FirmwareBirthdayMonth; - case Firm_BirthdayDay: return Config::FirmwareBirthdayDay; - case Firm_Color: return Config::FirmwareFavouriteColour; - - case AudioBitDepth: return Config::AudioBitDepth; - } - - return 0; -} - -bool GetConfigBool(ConfigEntry entry) -{ - switch (entry) - { -#ifdef JIT_ENABLED - case JIT_Enable: return Config::JIT_Enable != 0; - case JIT_LiteralOptimizations: return Config::JIT_LiteralOptimisations != 0; - case JIT_BranchOptimizations: return Config::JIT_BranchOptimisations != 0; - case JIT_FastMemory: return Config::JIT_FastMemory != 0; -#endif - - case ExternalBIOSEnable: return Config::ExternalBIOSEnable != 0; - - case DLDI_Enable: return Config::DLDIEnable != 0; - case DLDI_ReadOnly: return Config::DLDIReadOnly != 0; - case DLDI_FolderSync: return Config::DLDIFolderSync != 0; - - case DSiSD_Enable: return Config::DSiSDEnable != 0; - case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0; - case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0; - - case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0; - case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0; - } - - return false; -} - -std::string GetConfigString(ConfigEntry entry) -{ - switch (entry) - { - case DSi_NANDPath: return Config::DSiNANDPath; - - case DLDI_ImagePath: return Config::DLDISDPath; - case DLDI_FolderPath: return Config::DLDIFolderPath; - - case DSiSD_ImagePath: return Config::DSiSDPath; - case DSiSD_FolderPath: return Config::DSiSDFolderPath; - - case Firm_Username: return Config::FirmwareUsername; - case Firm_Message: return Config::FirmwareMessage; - case WifiSettingsPath: return Config::WifiSettingsPath; - } - - return ""; -} - -bool GetConfigArray(ConfigEntry entry, void* data) -{ - switch (entry) - { - case Firm_MAC: - { - std::string& mac_in = Config::FirmwareMAC; - u8* mac_out = (u8*)data; - - int o = 0; - u8 tmp = 0; - for (int i = 0; i < 18; i++) - { - char c = mac_in[i]; - if (c == '\0') break; - - int n; - if (c >= '0' && c <= '9') n = c - '0'; - else if (c >= 'a' && c <= 'f') n = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') n = c - 'A' + 10; - else continue; - - if (!(o & 1)) - tmp = n; - else - mac_out[o >> 1] = n | (tmp << 4); - - o++; - if (o >= 12) return true; - } - } - return false; - } - - return false; + return qmode; } constexpr char AccessMode(FileMode mode, bool file_exists) { + if (mode & FileMode::Append) + return 'a'; + if (!(mode & FileMode::Write)) // If we're only opening the file for reading... return 'r'; @@ -343,16 +281,21 @@ static std::string GetModeString(FileMode mode, bool file_exists) FileHandle* OpenFile(const std::string& path, FileMode mode) { - if ((mode & FileMode::ReadWrite) == FileMode::None) + if ((mode & (FileMode::ReadWrite | FileMode::Append)) == FileMode::None) { // If we aren't reading or writing, then we can't open the file Log(LogLevel::Error, "Attempted to open \"%s\" in neither read nor write mode (FileMode 0x%x)\n", path.c_str(), mode); return nullptr; } - bool file_exists = QFile::exists(QString::fromStdString(path)); - std::string modeString = GetModeString(mode, file_exists); + QString qpath{QString::fromStdString(path)}; + + std::string modeString = GetModeString(mode, QFile::exists(qpath)); + QIODevice::OpenMode qmode = GetQMode(mode); + QFile qfile{qpath}; + qfile.open(qmode); + FILE* file = fdopen(dup(qfile.handle()), modeString.c_str()); + qfile.close(); - FILE* file = fopen(path.c_str(), modeString.c_str()); if (file) { Log(LogLevel::Debug, "Opened \"%s\" with FileMode 0x%x (effective mode \"%s\")\n", path.c_str(), mode, modeString.c_str()); @@ -378,15 +321,7 @@ FileHandle* OpenLocalFile(const std::string& path, FileMode mode) } else { -#ifdef PORTABLE fullpath = QString::fromStdString(EmuDirectory) + QDir::separator() + qpath; -#else - // Check user configuration directory - QDir config(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)); - config.mkdir("melonDS"); - fullpath = config.absolutePath() + "/melonDS/"; - fullpath.append(qpath); -#endif } return OpenFile(fullpath.toStdString(), mode); @@ -423,6 +358,44 @@ bool LocalFileExists(const std::string& name) return true; } +bool CheckFileWritable(const std::string& filepath) +{ + FileHandle* file = Platform::OpenFile(filepath.c_str(), FileMode::Read); + + if (file) + { + // if the file exists, check if it can be opened for writing. + Platform::CloseFile(file); + file = Platform::OpenFile(filepath.c_str(), FileMode::Append); + if (file) + { + Platform::CloseFile(file); + return true; + } + else return false; + } + else + { + // if the file does not exist, create a temporary file to check, to avoid creating an empty file. + if (QTemporaryFile(filepath.c_str()).open()) + { + return true; + } + else return false; + } +} + +bool CheckLocalFileWritable(const std::string& name) +{ + FileHandle* file = Platform::OpenLocalFile(name.c_str(), FileMode::Append); + if (file) + { + Platform::CloseFile(file); + return true; + } + else return false; +} + bool FileSeek(FileHandle* file, s64 offset, FileSeekOrigin origin) { int stdorigin; @@ -578,37 +551,46 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen ROMManager::GBASave->RequestFlush(savedata, savelen, writeoffset, writelen); } -void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 writelen) +void WriteFirmware(const Firmware& firmware, u32 writeoffset, u32 writelen) { if (!ROMManager::FirmwareSave) return; - if (firmware.Header().Identifier != SPI_Firmware::GENERATED_FIRMWARE_IDENTIFIER) + if (firmware.GetHeader().Identifier != GENERATED_FIRMWARE_IDENTIFIER) { // If this is not the default built-in firmware... // ...then write the whole thing back. ROMManager::FirmwareSave->RequestFlush(firmware.Buffer(), firmware.Length(), writeoffset, writelen); } else { - u32 eapstart = firmware.ExtendedAccessPointOffset(); - u32 eapend = eapstart + sizeof(firmware.ExtendedAccessPoints()); + u32 eapstart = firmware.GetExtendedAccessPointOffset(); + u32 eapend = eapstart + sizeof(firmware.GetExtendedAccessPoints()); - u32 apstart = firmware.WifiAccessPointOffset(); - u32 apend = apstart + sizeof(firmware.AccessPoints()); + u32 apstart = firmware.GetWifiAccessPointOffset(); + u32 apend = apstart + sizeof(firmware.GetAccessPoints()); // assert that the extended access points come just before the regular ones assert(eapend == apstart); if (eapstart <= writeoffset && writeoffset < apend) { // If we're writing to the access points... - const u8* buffer = firmware.ExtendedAccessPointPosition(); - u32 length = sizeof(firmware.ExtendedAccessPoints()) + sizeof(firmware.AccessPoints()); + const u8* buffer = firmware.GetExtendedAccessPointPosition(); + u32 length = sizeof(firmware.GetExtendedAccessPoints()) + sizeof(firmware.GetAccessPoints()); ROMManager::FirmwareSave->RequestFlush(buffer, length, writeoffset - eapstart, writelen); } } } +void WriteDateTime(int year, int month, int day, int hour, int minute, int second) +{ + QDateTime hosttime = QDateTime::currentDateTime(); + QDateTime time = QDateTime(QDate(year, month, day), QTime(hour, minute, second)); + + Config::RTCOffset = hosttime.secsTo(time); + Config::Save(); +} + bool MP_Init() { return LocalMP::Init(); diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index 89f74e5f..3d47c45a 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,49 +22,66 @@ #include "SPI.h" #include "DSi_I2C.h" #include "NDS.h" +#include "DSi.h" #include "Config.h" #include "Platform.h" #include "types.h" #include +#include "main.h" + +using namespace melonDS; PowerManagementDialog* PowerManagementDialog::currentDlg = nullptr; -PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PowerManagementDialog) +PowerManagementDialog::PowerManagementDialog(QWidget* parent, EmuThread* emuThread) : QDialog(parent), emuThread(emuThread), ui(new Ui::PowerManagementDialog) { inited = false; ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - if (NDS::ConsoleType == 1) + if (emuThread->NDS->ConsoleType == 1) { ui->grpDSBattery->setEnabled(false); - oldDSiBatteryLevel = DSi_BPTWL::GetBatteryLevel(); - oldDSiBatteryCharging = DSi_BPTWL::GetBatteryCharging(); + auto& dsi = static_cast(*emuThread->NDS); + oldDSiBatteryLevel = dsi.I2C.GetBPTWL()->GetBatteryLevel(); + oldDSiBatteryCharging = dsi.I2C.GetBPTWL()->GetBatteryCharging(); } else { ui->grpDSiBattery->setEnabled(false); - oldDSBatteryLevel = SPI_Powerman::GetBatteryLevelOkay(); + oldDSBatteryLevel = emuThread->NDS->SPI.GetPowerMan()->GetBatteryLevelOkay(); } updateDSBatteryLevelControls(); - ui->cbDSiBatteryCharging->setChecked(DSi_BPTWL::GetBatteryCharging()); - int dsiBatterySliderPos; - switch (DSi_BPTWL::GetBatteryLevel()) + bool defaultDSiBatteryCharging = (emuThread->NDS->ConsoleType == 1) ? Config::DSiBatteryCharging : false; + + if (emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); + ui->cbDSiBatteryCharging->setChecked(dsi.I2C.GetBPTWL()->GetBatteryCharging()); + int dsiBatterySliderPos = 4; + switch (dsi.I2C.GetBPTWL()->GetBatteryLevel()) + { case DSi_BPTWL::batteryLevel_AlmostEmpty: dsiBatterySliderPos = 0; break; case DSi_BPTWL::batteryLevel_Low: dsiBatterySliderPos = 1; break; case DSi_BPTWL::batteryLevel_Half: dsiBatterySliderPos = 2; break; case DSi_BPTWL::batteryLevel_ThreeQuarters: dsiBatterySliderPos = 3; break; case DSi_BPTWL::batteryLevel_Full: dsiBatterySliderPos = 4; break; + } + ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos); } - ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos); + else + { + ui->cbDSiBatteryCharging->setChecked(Config::DSiBatteryCharging); + ui->sliderDSiBatteryLevel->setValue(Config::DSiBatteryLevel); + } + int inst = Platform::InstanceID(); if (inst > 0) @@ -84,26 +101,28 @@ void PowerManagementDialog::done(int r) { if (r == QDialog::Accepted) { - if (NDS::ConsoleType == 1) + if (emuThread->NDS->ConsoleType == 1) { - Config::DSiBatteryLevel = DSi_BPTWL::GetBatteryLevel(); - Config::DSiBatteryCharging = DSi_BPTWL::GetBatteryCharging(); + auto& dsi = static_cast(*emuThread->NDS); + Config::DSiBatteryLevel = dsi.I2C.GetBPTWL()->GetBatteryLevel(); + Config::DSiBatteryCharging = dsi.I2C.GetBPTWL()->GetBatteryCharging(); } else { - Config::DSBatteryLevelOkay = SPI_Powerman::GetBatteryLevelOkay(); + Config::DSBatteryLevelOkay = emuThread->NDS->SPI.GetPowerMan()->GetBatteryLevelOkay(); } } else { - if (NDS::ConsoleType == 1) + if (emuThread->NDS->ConsoleType == 1) { - DSi_BPTWL::SetBatteryLevel(oldDSiBatteryLevel); - DSi_BPTWL::SetBatteryCharging(oldDSiBatteryCharging); + auto& dsi = static_cast(*emuThread->NDS); + dsi.I2C.GetBPTWL()->SetBatteryLevel(oldDSiBatteryLevel); + dsi.I2C.GetBPTWL()->SetBatteryCharging(oldDSiBatteryCharging); } else { - SPI_Powerman::SetBatteryLevelOkay(oldDSBatteryLevel); + emuThread->NDS->SPI.GetPowerMan()->SetBatteryLevelOkay(oldDSBatteryLevel); } } @@ -114,17 +133,17 @@ void PowerManagementDialog::done(int r) void PowerManagementDialog::on_rbDSBatteryLow_clicked() { - SPI_Powerman::SetBatteryLevelOkay(false); + emuThread->NDS->SPI.GetPowerMan()->SetBatteryLevelOkay(false); } void PowerManagementDialog::on_rbDSBatteryOkay_clicked() { - SPI_Powerman::SetBatteryLevelOkay(true); + emuThread->NDS->SPI.GetPowerMan()->SetBatteryLevelOkay(true); } void PowerManagementDialog::updateDSBatteryLevelControls() { - if (SPI_Powerman::GetBatteryLevelOkay()) + if (emuThread->NDS->SPI.GetPowerMan()->GetBatteryLevelOkay()) ui->rbDSBatteryOkay->setChecked(true); else ui->rbDSBatteryLow->setChecked(true); @@ -132,23 +151,32 @@ void PowerManagementDialog::updateDSBatteryLevelControls() void PowerManagementDialog::on_cbDSiBatteryCharging_toggled() { - DSi_BPTWL::SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); + if (emuThread->NDS->ConsoleType == 1) + { + auto& dsi = static_cast(*emuThread->NDS); + dsi.I2C.GetBPTWL()->SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); + } } void PowerManagementDialog::on_sliderDSiBatteryLevel_valueChanged(int value) { if (!inited) return; - u8 newBatteryLevel; - switch (value) + if (emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); + u8 newBatteryLevel = DSi_BPTWL::batteryLevel_Full; + switch (value) + { case 0: newBatteryLevel = DSi_BPTWL::batteryLevel_AlmostEmpty; break; case 1: newBatteryLevel = DSi_BPTWL::batteryLevel_Low; break; case 2: newBatteryLevel = DSi_BPTWL::batteryLevel_Half; break; case 3: newBatteryLevel = DSi_BPTWL::batteryLevel_ThreeQuarters; break; case 4: newBatteryLevel = DSi_BPTWL::batteryLevel_Full; break; + } + dsi.I2C.GetBPTWL()->SetBatteryLevel(newBatteryLevel); } - DSi_BPTWL::SetBatteryLevel(newBatteryLevel); + updateDSBatteryLevelControls(); } diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h index f335a5ea..bc2abc3d 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -25,6 +25,7 @@ #include "types.h" namespace Ui { class PowerManagementDialog; } +class EmuThread; class PowerManagementDialog; class PowerManagementDialog : public QDialog @@ -32,11 +33,11 @@ class PowerManagementDialog : public QDialog Q_OBJECT public: - explicit PowerManagementDialog(QWidget* parent); + explicit PowerManagementDialog(QWidget* parent, EmuThread* emu_thread); ~PowerManagementDialog(); static PowerManagementDialog* currentDlg; - static PowerManagementDialog* openDlg(QWidget* parent) + static PowerManagementDialog* openDlg(QWidget* parent, EmuThread* emu_thread) { if (currentDlg) { @@ -44,7 +45,7 @@ public: return currentDlg; } - currentDlg = new PowerManagementDialog(parent); + currentDlg = new PowerManagementDialog(parent, emu_thread); currentDlg->open(); return currentDlg; } @@ -64,10 +65,11 @@ private slots: private: Ui::PowerManagementDialog* ui; + EmuThread* emuThread; bool inited; bool oldDSBatteryLevel; - u8 oldDSiBatteryLevel; + melonDS::u8 oldDSiBatteryLevel; bool oldDSiBatteryCharging; void updateDSBatteryLevelControls(); diff --git a/src/frontend/qt_sdl/QPathInput.h b/src/frontend/qt_sdl/QPathInput.h index beb618ca..fbead36a 100644 --- a/src/frontend/qt_sdl/QPathInput.h +++ b/src/frontend/qt_sdl/QPathInput.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp index b13ff022..5bff99ad 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.cpp +++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp @@ -21,18 +21,19 @@ #include "main.h" +using namespace melonDS; extern EmuThread* emuThread; -s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType) +s32 GetMainRAMValue(NDS& nds, const u32& addr, const ramInfo_ByteType& byteType) { switch (byteType) { case ramInfo_OneByte: - return *(s8*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + return *(s8*)(nds.MainRAM + (addr&nds.MainRAMMask)); case ramInfo_TwoBytes: - return *(s16*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + return *(s16*)(nds.MainRAM + (addr&nds.MainRAMMask)); case ramInfo_FourBytes: - return *(s32*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + return *(s32*)(nds.MainRAM + (addr&nds.MainRAMMask)); default: return 0; } @@ -40,7 +41,7 @@ s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType) RAMInfoDialog* RAMInfoDialog::currentDlg = nullptr; -RAMInfoDialog::RAMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::RAMInfoDialog) +RAMInfoDialog::RAMInfoDialog(QWidget* parent, EmuThread* emuThread) : QDialog(parent), emuThread(emuThread), ui(new Ui::RAMInfoDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -90,7 +91,7 @@ void RAMInfoDialog::ShowRowsInTable() for (u32 row = scrollValue; row < std::min(scrollValue+25, RowDataVector->size()); row++) { ramInfo_RowData& rowData = RowDataVector->at(row); - rowData.Update(SearchThread->GetSearchByteType()); + rowData.Update(*emuThread->NDS, SearchThread->GetSearchByteType()); if (ui->ramTable->item(row, ramInfo_Address) == nullptr) { @@ -185,7 +186,7 @@ void RAMInfoDialog::on_ramTable_itemChanged(QTableWidgetItem *item) s32 itemValue = item->text().toInt(); if (rowData.Value != itemValue) - rowData.SetValue(itemValue); + rowData.SetValue(*emuThread->NDS, itemValue); } /** @@ -240,14 +241,14 @@ void RAMSearchThread::run() if (SearchMode == ramInfoSTh_SearchAll || RowDataVector->size() == 0) { // First search mode - for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+NDS::MainRAMMaxSize; addr += SearchByteType) + for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+MainRAMMaxSize; addr += SearchByteType) { - const s32& value = GetMainRAMValue(addr, SearchByteType); + const s32& value = GetMainRAMValue(*emuThread->NDS, addr, SearchByteType); RowDataVector->push_back({ addr, value, value }); // A solution to prevent to call too many slot. - u32 newProgress = (int)((addr-0x02000000) / (NDS::MainRAMMaxSize-1.0f) * 100); + u32 newProgress = (int)((addr-0x02000000) / (MainRAMMaxSize-1.0f) * 100); if (progress < newProgress) { progress = newProgress; @@ -263,7 +264,7 @@ void RAMSearchThread::run() for (u32 row = 0; SearchRunning && row < RowDataVector->size(); row++) { const u32& addr = RowDataVector->at(row).Address; - const s32& value = GetMainRAMValue(addr, SearchByteType); + const s32& value = GetMainRAMValue(*emuThread->NDS, addr, SearchByteType); if (SearchValue == value) newRowDataVector->push_back({ addr, value, value }); diff --git a/src/frontend/qt_sdl/RAMInfoDialog.h b/src/frontend/qt_sdl/RAMInfoDialog.h index 33afd988..2a5b1620 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.h +++ b/src/frontend/qt_sdl/RAMInfoDialog.h @@ -32,6 +32,7 @@ namespace Ui { class RAMInfoDialog; } class RAMInfoDialog; class RAMSearchThread; class RAMUpdateThread; +class EmuThread; enum ramInfo_ByteType { @@ -53,22 +54,22 @@ enum ramInfo_Previous }; -s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType); +melonDS::s32 GetMainRAMValue(melonDS::NDS& nds, const melonDS::u32& addr, const ramInfo_ByteType& byteType); struct ramInfo_RowData { - u32 Address; - s32 Value; - s32 Previous; + melonDS::u32 Address; + melonDS::s32 Value; + melonDS::s32 Previous; - void Update(const ramInfo_ByteType& byteType) + void Update(melonDS::NDS& nds, const ramInfo_ByteType& byteType) { - Value = GetMainRAMValue(Address, byteType); + Value = GetMainRAMValue(nds, Address, byteType); } - void SetValue(const s32& value) + void SetValue(melonDS::NDS& nds, const melonDS::s32& value) { - NDS::MainRAM[Address&NDS::MainRAMMask] = (u32)value; + nds.MainRAM[Address&nds.MainRAMMask] = (melonDS::u32)value; Value = value; } }; @@ -78,11 +79,11 @@ class RAMInfoDialog : public QDialog Q_OBJECT public: - explicit RAMInfoDialog(QWidget* parent); + explicit RAMInfoDialog(QWidget* parent, EmuThread* emuThread); ~RAMInfoDialog(); static RAMInfoDialog* currentDlg; - static RAMInfoDialog* openDlg(QWidget* parent) + static RAMInfoDialog* openDlg(QWidget* parent, EmuThread* emuThread) { if (currentDlg) { @@ -90,7 +91,7 @@ public: return currentDlg; } - currentDlg = new RAMInfoDialog(parent); + currentDlg = new RAMInfoDialog(parent, emuThread); currentDlg->show(); return currentDlg; } @@ -99,7 +100,7 @@ public: currentDlg = nullptr; } - s32 SearchValue = 0; + melonDS::s32 SearchValue = 0; void ClearTableContents(); @@ -115,9 +116,10 @@ private slots: void OnSearchFinished(); void ShowRowsInTable(); - void SetProgressbarValue(const u32& value); + void SetProgressbarValue(const melonDS::u32& value); private: + EmuThread* emuThread; Ui::RAMInfoDialog* ui; RAMSearchThread* SearchThread; @@ -132,7 +134,7 @@ public: explicit RAMSearchThread(RAMInfoDialog* dialog); ~RAMSearchThread() override; - void Start(const s32& searchValue, const ramInfoSTh_SearchMode& searchMode = ramInfoSTh_Default); + void Start(const melonDS::s32& searchValue, const ramInfoSTh_SearchMode& searchMode = ramInfoSTh_Default); void Start(const ramInfoSTh_SearchMode& searchMode); void SetSearchByteType(const ramInfo_ByteType& bytetype); @@ -148,14 +150,14 @@ private: bool SearchRunning = false; ramInfoSTh_SearchMode SearchMode; - s32 SearchValue; + melonDS::s32 SearchValue; ramInfo_ByteType SearchByteType = ramInfo_OneByte; std::vector* RowDataVector = nullptr; void ClearTableContents(); signals: - void SetProgressbarValue(const u32& value); + void SetProgressbarValue(const melonDS::u32& value); }; #endif // RAMINFODIALOG_H diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 4be4cb0c..0f10b2f5 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,11 +21,15 @@ #include +#include "gif-h/gif.h" + #include "NDS.h" #include "NDSCart.h" #include "Platform.h" #include "Config.h" +using namespace melonDS; + QString IntToHex(u64 num) { return ("0x" + QString::number(num, 16).toUpper()); @@ -38,30 +42,30 @@ QString QStringBytes(u64 num) ROMInfoDialog* ROMInfoDialog::currentDlg = nullptr; -ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMInfoDialog) +ROMInfoDialog::ROMInfoDialog(QWidget* parent, const melonDS::NDSCart::CartCommon& rom) : QDialog(parent), ui(new Ui::ROMInfoDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - const NDSBanner* banner = NDSCart::Cart->Banner(); - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSBanner* banner = rom.Banner(); + const NDSHeader& header = rom.GetHeader(); u32 iconData[32 * 32]; ROMManager::ROMIcon(banner->Icon, banner->Palette, iconData); - iconImage = QImage(reinterpret_cast(iconData), 32, 32, QImage::Format_ARGB32).copy(); + iconImage = QImage(reinterpret_cast(iconData), 32, 32, QImage::Format_RGBA8888).copy(); ui->iconImage->setPixmap(QPixmap::fromImage(iconImage)); if (banner->Version == 0x103) { - u32 animatedIconData[32 * 32 * 64] = {0}; + ui->saveAnimatedIconButton->setEnabled(true); + ROMManager::AnimatedROMIcon(banner->DSiIcon, banner->DSiPalette, banner->DSiSequence, animatedIconData, animatedSequence); - for (int i = 0; i < 64; i++) + for (u32* image: animatedIconData) { - if (animatedIconData[32 * 32 * i] == 0) + if (!image) break; - animatedIconImages.push_back(QPixmap::fromImage(QImage(reinterpret_cast(&animatedIconData[32 * 32 * i]), 32, 32, QImage::Format_ARGB32).copy())); + animatedIconImages.push_back(QPixmap::fromImage(QImage(reinterpret_cast(image), 32, 32, QImage::Format_RGBA8888).copy())); } - iconTimeline = new QTimeLine(animatedSequence.size() / 60 * 1000, this); iconTimeline->setFrameRange(0, animatedSequence.size() - 1); iconTimeline->setLoopCount(0); @@ -139,6 +143,30 @@ void ROMInfoDialog::on_saveIconButton_clicked() iconImage.save(filename, "PNG"); } +void ROMInfoDialog::on_saveAnimatedIconButton_clicked() +{ + QString filename = QFileDialog::getSaveFileName(this, + "Save Animated Icon", + QString::fromStdString(Config::LastROMFolder), + "GIF Images (*.gif)"); + if (filename.isEmpty()) + return; + + + GifWriter writer; + + // The GIF format only supports delays of 0.01 seconds, so 0.0166... (60fps) + // is rounded up to 0.02 (50fps) + GifBegin(&writer, filename.toStdString().c_str(), 32, 32, 2); + for (int i: animatedSequence) + { + if (animatedIconData[i] == 0) + break; + GifWriteFrame(&writer, reinterpret_cast(animatedIconData[i]), 32, 32, 2); + } + GifEnd(&writer); +} + void ROMInfoDialog::iconSetFrame(int frame) { ui->dsiIconImage->setPixmap(animatedIconImages[animatedSequence[frame]]); diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index fd036a02..f7e3b5f5 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -29,17 +29,17 @@ namespace Ui { class ROMInfoDialog; } class ROMInfoDialog; - +namespace melonDS::NDSCart { class CartCommon; } class ROMInfoDialog : public QDialog { Q_OBJECT public: - explicit ROMInfoDialog(QWidget* parent); + explicit ROMInfoDialog(QWidget* parent, const melonDS::NDSCart::CartCommon& rom); ~ROMInfoDialog(); static ROMInfoDialog* currentDlg; - static ROMInfoDialog* openDlg(QWidget* parent) + static ROMInfoDialog* openDlg(QWidget* parent, const melonDS::NDSCart::CartCommon& rom) { if (currentDlg) { @@ -47,7 +47,7 @@ public: return currentDlg; } - currentDlg = new ROMInfoDialog(parent); + currentDlg = new ROMInfoDialog(parent, rom); currentDlg->open(); return currentDlg; } @@ -60,6 +60,7 @@ private slots: void done(int r); void on_saveIconButton_clicked(); + void on_saveAnimatedIconButton_clicked(); void iconSetFrame(int frame); @@ -68,6 +69,7 @@ private: QImage iconImage; QTimeLine* iconTimeline; + melonDS::u32 animatedIconData[64][32*32] = {0}; std::vector animatedIconImages; std::vector animatedSequence; }; diff --git a/src/frontend/qt_sdl/ROMInfoDialog.ui b/src/frontend/qt_sdl/ROMInfoDialog.ui index 1c9d844b..25592889 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.ui +++ b/src/frontend/qt_sdl/ROMInfoDialog.ui @@ -6,8 +6,8 @@ 0 0 - 559 - 532 + 557 + 547 @@ -525,10 +525,17 @@ + + + + Save icon + + + - + 0 0 @@ -619,7 +626,7 @@ 1 - + @@ -644,16 +651,42 @@ + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + - + - + 0 0 @@ -724,25 +757,40 @@ - + + + + false + + + Save animated icon + + + + Qt::Horizontal + + + 0 + 0 + + - - - - Save icon - - - - + Qt::Horizontal + + + 0 + 0 + + diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 49f4c457..9607c848 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -28,6 +28,9 @@ #include #include +#include +#include + #include #ifdef ARCHIVE_SUPPORT_ENABLED #include "ArchiveUtil.h" @@ -39,15 +42,19 @@ #include "NDS.h" #include "DSi.h" #include "SPI.h" +#include "RTC.h" #include "DSi_I2C.h" #include "FreeBIOS.h" +#include "main.h" using std::make_unique; using std::pair; using std::string; using std::tie; using std::unique_ptr; -using namespace Platform; +using std::wstring_convert; +using namespace melonDS; +using namespace melonDS::Platform; namespace ROMManager { @@ -62,8 +69,8 @@ std::string BaseGBAROMDir = ""; std::string BaseGBAROMName = ""; std::string BaseGBAAssetName = ""; -SaveManager* NDSSave = nullptr; -SaveManager* GBASave = nullptr; +std::unique_ptr NDSSave = nullptr; +std::unique_ptr GBASave = nullptr; std::unique_ptr FirmwareSave = nullptr; std::unique_ptr BackupState = nullptr; @@ -204,6 +211,9 @@ QString VerifyDSFirmware() f = Platform::OpenLocalFile(Config::FirmwarePath, FileMode::Read); if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings."; + if (!Platform::CheckFileWritable(Config::FirmwarePath)) + return "DS firmware is unable to be written to.\nPlease check file/folder write permissions."; + len = FileLength(f); if (len == 0x20000) { @@ -231,6 +241,9 @@ QString VerifyDSiFirmware() f = Platform::OpenLocalFile(Config::DSiFirmwarePath, FileMode::Read); if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings."; + if (!Platform::CheckFileWritable(Config::FirmwarePath)) + return "DSi firmware is unable to be written to.\nPlease check file/folder write permissions."; + len = FileLength(f); if (len != 0x20000) { @@ -253,6 +266,9 @@ QString VerifyDSiNAND() f = Platform::OpenLocalFile(Config::DSiNANDPath, FileMode::ReadWriteExisting); if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings."; + if (!Platform::CheckFileWritable(Config::FirmwarePath)) + return "DSi NAND is unable to be written to.\nPlease check file/folder write permissions."; + // TODO: some basic checks // check that it has the nocash footer, and all @@ -297,6 +313,28 @@ QString VerifySetup() return ""; } +std::string GetEffectiveFirmwareSavePath(EmuThread* thread) +{ + if (!Config::ExternalBIOSEnable) + { + return Config::WifiSettingsPath; + } + if (thread->NDS->ConsoleType == 1) + { + return Config::DSiFirmwarePath; + } + else + { + return Config::FirmwarePath; + } +} + +// Initializes the firmware save manager with the selected firmware image's path +// OR the path to the wi-fi settings. +void InitFirmwareSaveManager(EmuThread* thread) noexcept +{ + FirmwareSave = std::make_unique(GetEffectiveFirmwareSavePath(thread)); +} std::string GetSavestateName(int slot) { @@ -311,7 +349,7 @@ bool SavestateExists(int slot) return Platform::FileExists(ssfile); } -bool LoadState(const std::string& filename) +bool LoadState(NDS& nds, const std::string& filename) { FILE* file = fopen(filename.c_str(), "rb"); if (file == nullptr) @@ -328,7 +366,7 @@ bool LoadState(const std::string& filename) return false; } - if (!NDS::DoSavestate(backup.get()) || backup->Error) + if (!nds.DoSavestate(backup.get()) || backup->Error) { // Back up the emulator's state. If that failed... Platform::Log(Platform::LogLevel::Error, "Failed to back up state, aborting load (from \"%s\")\n", filename.c_str()); fclose(file); @@ -360,7 +398,7 @@ bool LoadState(const std::string& filename) // Get ready to load the state from the buffer into the emulator std::unique_ptr state = std::make_unique(buffer.data(), size, false); - if (!NDS::DoSavestate(state.get()) || state->Error) + if (!nds.DoSavestate(state.get()) || state->Error) { // If we couldn't load the savestate from the buffer... Platform::Log(Platform::LogLevel::Error, "Failed to load state file \"%s\" into emulator\n", filename.c_str()); return false; @@ -385,7 +423,7 @@ bool LoadState(const std::string& filename) return true; } -bool SaveState(const std::string& filename) +bool SaveState(NDS& nds, const std::string& filename) { FILE* file = fopen(filename.c_str(), "wb"); @@ -402,7 +440,7 @@ bool SaveState(const std::string& filename) } // Write the savestate to the in-memory buffer - NDS::DoSavestate(&state); + nds.DoSavestate(&state); if (state.Error) { @@ -434,7 +472,7 @@ bool SaveState(const std::string& filename) return true; } -void UndoStateLoad() +void UndoStateLoad(NDS& nds) { if (!SavestateLoaded || !BackupState) return; @@ -443,7 +481,7 @@ void UndoStateLoad() // pray that this works // what do we do if it doesn't??? // but it should work. - NDS::DoSavestate(BackupState.get()); + nds.DoSavestate(BackupState.get()); if (NDSSave && (!PreviousSaveFile.empty())) { @@ -452,121 +490,340 @@ void UndoStateLoad() } -void UnloadCheats() +void UnloadCheats(NDS& nds) { if (CheatFile) { delete CheatFile; CheatFile = nullptr; - AREngine::SetCodeFile(nullptr); + nds.AREngine.SetCodeFile(nullptr); } } -void LoadCheats() +void LoadCheats(NDS& nds) { - UnloadCheats(); + UnloadCheats(nds); std::string filename = GetAssetPath(false, Config::CheatFilePath, ".mch"); // TODO: check for error (malformed cheat file, ...) CheatFile = new ARCodeFile(filename); - AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); + nds.AREngine.SetCodeFile(CheatsOn ? CheatFile : nullptr); } -void LoadBIOSFiles() +std::unique_ptr LoadARM9BIOS() noexcept { - if (Config::ExternalBIOSEnable) + if (!Config::ExternalBIOSEnable) { - if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS9Path, FileMode::Read)) - { - FileRewind(f); - FileRead(NDS::ARM9BIOS, sizeof(NDS::ARM9BIOS), 1, f); - - Log(LogLevel::Info, "ARM9 BIOS loaded from %s\n", Config::BIOS9Path.c_str()); - Platform::CloseFile(f); - } - else - { - Log(LogLevel::Warn, "ARM9 BIOS not found\n"); - - for (int i = 0; i < 16; i++) - ((u32*)NDS::ARM9BIOS)[i] = 0xE7FFDEFF; - } - - if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS7Path, FileMode::Read)) - { - FileRead(NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS), 1, f); - - Log(LogLevel::Info, "ARM7 BIOS loaded from\n", Config::BIOS7Path.c_str()); - Platform::CloseFile(f); - } - else - { - Log(LogLevel::Warn, "ARM7 BIOS not found\n"); - - for (int i = 0; i < 16; i++) - ((u32*)NDS::ARM7BIOS)[i] = 0xE7FFDEFF; - } - } - else - { - Log(LogLevel::Info, "Using built-in ARM7 and ARM9 BIOSes\n"); - memcpy(NDS::ARM9BIOS, bios_arm9_bin, sizeof(bios_arm9_bin)); - memcpy(NDS::ARM7BIOS, bios_arm7_bin, sizeof(bios_arm7_bin)); + return Config::ConsoleType == 0 ? std::make_unique(bios_arm9_bin) : nullptr; } - if (Config::ConsoleType == 1) + if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read)) { - if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS9Path, FileMode::Read)) - { - FileRead(DSi::ARM9iBIOS, sizeof(DSi::ARM9iBIOS), 1, f); + std::unique_ptr bios = std::make_unique(); + FileRewind(f); + FileRead(bios->data(), bios->size(), 1, f); + CloseFile(f); + Log(Info, "ARM9 BIOS loaded from %s\n", Config::BIOS9Path.c_str()); + return bios; + } - Log(LogLevel::Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str()); - Platform::CloseFile(f); - } - else - { - Log(LogLevel::Warn, "ARM9i BIOS not found\n"); + Log(Warn, "ARM9 BIOS not found\n"); + return nullptr; +} - for (int i = 0; i < 16; i++) - ((u32*)DSi::ARM9iBIOS)[i] = 0xE7FFDEFF; - } +std::unique_ptr LoadARM7BIOS() noexcept +{ + if (!Config::ExternalBIOSEnable) + { + return Config::ConsoleType == 0 ? std::make_unique(bios_arm7_bin) : nullptr; + } - if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS7Path, FileMode::Read)) - { - // TODO: check if the first 32 bytes are crapoed - FileRead(DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS), 1, f); + if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read)) + { + std::unique_ptr bios = std::make_unique(); + FileRead(bios->data(), bios->size(), 1, f); + CloseFile(f); + Log(Info, "ARM7 BIOS loaded from %s\n", Config::BIOS7Path.c_str()); + return bios; + } - Log(LogLevel::Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str()); - CloseFile(f); - } - else - { - Log(LogLevel::Warn, "ARM7i BIOS not found\n"); + Log(Warn, "ARM7 BIOS not found\n"); + return nullptr; +} - for (int i = 0; i < 16; i++) - ((u32*)DSi::ARM7iBIOS)[i] = 0xE7FFDEFF; - } +std::unique_ptr LoadDSiARM9BIOS() noexcept +{ + if (FileHandle* f = OpenLocalFile(Config::DSiBIOS9Path, Read)) + { + std::unique_ptr bios = std::make_unique(); + FileRead(bios->data(), bios->size(), 1, f); + CloseFile(f); if (!Config::DSiFullBIOSBoot) { // herp - *(u32*)&DSi::ARM9iBIOS[0] = 0xEAFFFFFE; - *(u32*)&DSi::ARM7iBIOS[0] = 0xEAFFFFFE; + *(u32*)bios->data() = 0xEAFFFFFE; // overwrites the reset vector // TODO!!!! // hax the upper 32K out of the goddamn DSi // done that :) -pcy } + Log(Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str()); + return bios; } + + Log(Warn, "ARM9i BIOS not found\n"); + return nullptr; } -void EnableCheats(bool enable) +std::unique_ptr LoadDSiARM7BIOS() noexcept +{ + if (FileHandle* f = OpenLocalFile(Config::DSiBIOS7Path, Read)) + { + std::unique_ptr bios = std::make_unique(); + FileRead(bios->data(), bios->size(), 1, f); + CloseFile(f); + + if (!Config::DSiFullBIOSBoot) + { + // herp + *(u32*)bios->data() = 0xEAFFFFFE; // overwrites the reset vector + + // TODO!!!! + // hax the upper 32K out of the goddamn DSi + // done that :) -pcy + } + Log(Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str()); + return bios; + } + + Log(Warn, "ARM7i BIOS not found\n"); + return nullptr; +} + +Firmware GenerateFirmware(int type) noexcept +{ + // Construct the default firmware... + string settingspath; + Firmware firmware = Firmware(type); + assert(firmware.Buffer() != nullptr); + + // If using generated firmware, we keep the wi-fi settings on the host disk separately. + // Wi-fi access point data includes Nintendo WFC settings, + // and if we didn't keep them then the player would have to reset them in each session. + // We don't need to save the whole firmware, just the part that may actually change. + if (FileHandle* f = OpenLocalFile(Config::WifiSettingsPath, Read)) + {// If we have Wi-fi settings to load... + constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(Firmware::WifiAccessPoint) + sizeof(Firmware::ExtendedWifiAccessPoint)); + + if (!FileRead(firmware.GetExtendedAccessPointPosition(), TOTAL_WFC_SETTINGS_SIZE, 1, f)) + { // If we couldn't read the Wi-fi settings from this file... + Log(Warn, "Failed to read Wi-fi settings from \"%s\"; using defaults instead\n", Config::WifiSettingsPath.c_str()); + + // The access point and extended access point segments might + // be in different locations depending on the firmware revision, + // but our generated firmware always keeps them next to each other. + // (Extended access points first, then regular ones.) + firmware.GetAccessPoints() = { + Firmware::WifiAccessPoint(type), + Firmware::WifiAccessPoint(), + Firmware::WifiAccessPoint(), + }; + + firmware.GetExtendedAccessPoints() = { + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), + }; + firmware.UpdateChecksums(); + CloseFile(f); + } + } + + CustomizeFirmware(firmware, true); + + // If we don't have Wi-fi settings to load, + // then the defaults will have already been populated by the constructor. + return firmware; +} + +std::optional LoadFirmware(int type) noexcept +{ + if (!Config::ExternalBIOSEnable) + { // If we're using built-in firmware... + if (type == 1) + { + Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n"); + return std::nullopt; + } + + return GenerateFirmware(type); + } + const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath; + + Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); + + FileHandle* file = OpenLocalFile(firmwarepath, Read); + + if (!file) + { + Log(Error, "SPI firmware: couldn't open firmware file!\n"); + return std::nullopt; + } + Firmware firmware(file); + CloseFile(file); + + if (!firmware.Buffer()) + { + Log(Error, "SPI firmware: couldn't read firmware file!\n"); + return std::nullopt; + } + + CustomizeFirmware(firmware, Config::FirmwareOverrideSettings); + + return firmware; +} + + +std::optional LoadNAND(const std::array& arm7ibios) noexcept +{ + FileHandle* nandfile = OpenLocalFile(Config::DSiNANDPath, ReadWriteExisting); + if (!nandfile) + return std::nullopt; + + DSi_NAND::NANDImage nandImage(nandfile, &arm7ibios[0x8308]); + if (!nandImage) + { + Log(Error, "Failed to parse DSi NAND\n"); + return std::nullopt; + // the NANDImage takes ownership of the FileHandle, no need to clean it up here + } + + // scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage + { + auto mount = DSi_NAND::NANDMount(nandImage); + if (!mount) + { + Log(Error, "Failed to mount DSi NAND\n"); + return std::nullopt; + } + + DSi_NAND::DSiFirmwareSystemSettings settings {}; + if (!mount.ReadUserData(settings)) + { + Log(Error, "Failed to read DSi NAND user data\n"); + return std::nullopt; + } + + // override user settings, if needed + if (Config::FirmwareOverrideSettings) + { + // we store relevant strings as UTF-8, so we need to convert them to UTF-16 + auto converter = wstring_convert, char16_t>{}; + + // setting up username + std::u16string username = converter.from_bytes(Config::FirmwareUsername); + size_t usernameLength = std::min(username.length(), (size_t) 10); + memset(&settings.Nickname, 0, sizeof(settings.Nickname)); + memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t)); + + // setting language + settings.Language = static_cast(Config::FirmwareLanguage); + + // setting up color + settings.FavoriteColor = Config::FirmwareFavouriteColour; + + // setting up birthday + settings.BirthdayMonth = Config::FirmwareBirthdayMonth; + settings.BirthdayDay = Config::FirmwareBirthdayDay; + + // setup message + std::u16string message = converter.from_bytes(Config::FirmwareMessage); + size_t messageLength = std::min(message.length(), (size_t) 26); + memset(&settings.Message, 0, sizeof(settings.Message)); + memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t)); + + // TODO: make other items configurable? + } + + // fix touchscreen coords + settings.TouchCalibrationADC1 = {0, 0}; + settings.TouchCalibrationPixel1 = {0, 0}; + settings.TouchCalibrationADC2 = {255 << 4, 191 << 4}; + settings.TouchCalibrationPixel2 = {255, 191}; + + settings.UpdateHash(); + + if (!mount.ApplyUserData(settings)) + { + Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n"); + return std::nullopt; + } + } + + return nandImage; +} + +constexpr u64 MB(u64 i) +{ + return i * 1024 * 1024; +} + +constexpr u64 imgsizes[] = {0, MB(256), MB(512), MB(1024), MB(2048), MB(4096)}; +std::optional GetDSiSDCardArgs() noexcept +{ + if (!Config::DSiSDEnable) + return std::nullopt; + + return FATStorageArgs { + Config::DSiSDPath, + imgsizes[Config::DSiSDSize], + Config::DSiSDReadOnly, + Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt + }; +} + +std::optional LoadDSiSDCard() noexcept +{ + if (!Config::DSiSDEnable) + return std::nullopt; + + return FATStorage( + Config::DSiSDPath, + imgsizes[Config::DSiSDSize], + Config::DSiSDReadOnly, + Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt + ); +} + +std::optional GetDLDISDCardArgs() noexcept +{ + if (!Config::DLDIEnable) + return std::nullopt; + + return FATStorageArgs{ + Config::DLDISDPath, + imgsizes[Config::DLDISize], + Config::DLDIReadOnly, + Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt + }; +} + +std::optional LoadDLDISDCard() noexcept +{ + if (!Config::DLDIEnable) + return std::nullopt; + + return FATStorage(*GetDLDISDCardArgs()); +} + +void EnableCheats(NDS& nds, bool enable) { CheatsOn = enable; if (CheatFile) - AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); + nds.AREngine.SetCodeFile(CheatsOn ? CheatFile : nullptr); } ARCodeFile* GetCheatFile() @@ -575,28 +832,38 @@ ARCodeFile* GetCheatFile() } -void SetBatteryLevels() +void SetBatteryLevels(NDS& nds) { - if (NDS::ConsoleType == 1) + if (nds.ConsoleType == 1) { - DSi_BPTWL::SetBatteryLevel(Config::DSiBatteryLevel); - DSi_BPTWL::SetBatteryCharging(Config::DSiBatteryCharging); + auto& dsi = static_cast(nds); + dsi.I2C.GetBPTWL()->SetBatteryLevel(Config::DSiBatteryLevel); + dsi.I2C.GetBPTWL()->SetBatteryCharging(Config::DSiBatteryCharging); } else { - SPI_Powerman::SetBatteryLevelOkay(Config::DSBatteryLevelOkay); + nds.SPI.GetPowerMan()->SetBatteryLevelOkay(Config::DSBatteryLevelOkay); } } -void Reset() +void SetDateTime(NDS& nds) { - NDS::SetConsoleType(Config::ConsoleType); - if (Config::ConsoleType == 1) EjectGBACart(); - LoadBIOSFiles(); + QDateTime hosttime = QDateTime::currentDateTime(); + QDateTime time = hosttime.addSecs(Config::RTCOffset); - InstallFirmware(); - NDS::Reset(); - SetBatteryLevels(); + nds.RTC.SetDateTime(time.date().year(), time.date().month(), time.date().day(), + time.time().hour(), time.time().minute(), time.time().second()); +} + +void Reset(EmuThread* thread) +{ + thread->UpdateConsole(Keep {}, Keep {}); + + if (Config::ConsoleType == 1) EjectGBACart(*thread->NDS); + + thread->NDS->Reset(); + SetBatteryLevels(*thread->NDS); + SetDateTime(*thread->NDS); if ((CartType != -1) && NDSSave) { @@ -616,6 +883,7 @@ void Reset() GBASave->SetPath(newsave, false); } + InitFirmwareSaveManager(thread); if (FirmwareSave) { std::string oldsave = FirmwareSave->GetPath(); @@ -640,40 +908,35 @@ void Reset() if (!BaseROMName.empty()) { - if (Config::DirectBoot || NDS::NeedsDirectBoot()) + if (Config::DirectBoot || thread->NDS->NeedsDirectBoot()) { - NDS::SetupDirectBoot(BaseROMName); + thread->NDS->SetupDirectBoot(BaseROMName); } } + + thread->NDS->Start(); } -bool LoadBIOS() +bool BootToMenu(EmuThread* thread) { - NDS::SetConsoleType(Config::ConsoleType); - - LoadBIOSFiles(); - - if (!InstallFirmware()) + // Keep whatever cart is in the console, if any. + if (!thread->UpdateConsole(Keep {}, Keep {})) + // Try to update the console, but keep the existing cart. If that fails... return false; - if (NDS::NeedsDirectBoot()) + // BIOS and firmware files are loaded, patched, and installed in UpdateConsole + if (thread->NDS->NeedsDirectBoot()) return false; - /*if (NDSSave) delete NDSSave; - NDSSave = nullptr; - - CartType = -1; - BaseROMDir = ""; - BaseROMName = ""; - BaseAssetName = "";*/ - - NDS::Reset(); - SetBatteryLevels(); + InitFirmwareSaveManager(thread); + thread->NDS->Reset(); + SetBatteryLevels(*thread->NDS); + SetDateTime(*thread->NDS); return true; } -u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) +u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr& outContent) { u64 realSize = ZSTD_getFrameContentSize(inContent, inSize); const u32 maxSize = 0x40000000; @@ -685,16 +948,16 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) if (realSize != ZSTD_CONTENTSIZE_UNKNOWN) { - u8* realContent = new u8[realSize]; - u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize); + auto newOutContent = make_unique(realSize); + u64 decompressed = ZSTD_decompress(newOutContent.get(), realSize, inContent, inSize); if (ZSTD_isError(decompressed)) { - delete[] realContent; + outContent = nullptr; return 0; } - *outContent = realContent; + outContent = std::move(newOutContent); return realSize; } else @@ -749,9 +1012,8 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) } } while (inBuf.pos < inBuf.size); - ZSTD_freeDStream(dStream); - *outContent = new u8[outBuf.pos]; - memcpy(*outContent, outBuf.dst, outBuf.pos); + outContent = make_unique(outBuf.pos); + memcpy(outContent.get(), outBuf.dst, outBuf.pos); ZSTD_freeDStream(dStream); free(outBuf.dst); @@ -768,45 +1030,8 @@ void ClearBackupState() } } -// We want both the firmware object and the path that was used to load it, -// since we'll need to give it to the save manager later -pair, string> LoadFirmwareFromFile() +pair, string> GenerateDefaultFirmware() { - string loadedpath; - unique_ptr firmware = nullptr; - string firmwarepath = Config::ConsoleType == 0 ? Config::FirmwarePath : Config::DSiFirmwarePath; - - Log(LogLevel::Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); - - string firmwareinstancepath = firmwarepath + Platform::InstanceFileSuffix(); - - loadedpath = firmwareinstancepath; - FileHandle* f = Platform::OpenLocalFile(firmwareinstancepath, FileMode::Read); - if (!f) - { - loadedpath = firmwarepath; - f = Platform::OpenLocalFile(firmwarepath, FileMode::Read); - } - - if (f) - { - firmware = make_unique(f); - if (!firmware->Buffer()) - { - Log(LogLevel::Warn, "Couldn't read firmware file!\n"); - firmware = nullptr; - loadedpath = ""; - } - - CloseFile(f); - } - - return std::make_pair(std::move(firmware), loadedpath); -} - -pair, string> GenerateDefaultFirmware() -{ - using namespace SPI_Firmware; // Construct the default firmware... string settingspath; std::unique_ptr firmware = std::make_unique(Config::ConsoleType); @@ -814,7 +1039,7 @@ pair, string> GenerateDefaultFirmware() // Try to open the instanced Wi-fi settings, falling back to the regular Wi-fi settings if they don't exist. // We don't need to save the whole firmware, just the part that may actually change. - std::string wfcsettingspath = Platform::GetConfigString(ConfigEntry::WifiSettingsPath); + std::string wfcsettingspath = Config::WifiSettingsPath; settingspath = wfcsettingspath + Platform::InstanceFileSuffix(); FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read); if (!f) @@ -828,27 +1053,27 @@ pair, string> GenerateDefaultFirmware() // and if we didn't keep them then the player would have to reset them in each session. if (f) { // If we have Wi-fi settings to load... - constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(WifiAccessPoint) + sizeof(ExtendedWifiAccessPoint)); + constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(Firmware::WifiAccessPoint) + sizeof(Firmware::ExtendedWifiAccessPoint)); // The access point and extended access point segments might // be in different locations depending on the firmware revision, // but our generated firmware always keeps them next to each other. // (Extended access points first, then regular ones.) - if (!FileRead(firmware->ExtendedAccessPointPosition(), TOTAL_WFC_SETTINGS_SIZE, 1, f)) + if (!FileRead(firmware->GetExtendedAccessPointPosition(), TOTAL_WFC_SETTINGS_SIZE, 1, f)) { // If we couldn't read the Wi-fi settings from this file... Platform::Log(Platform::LogLevel::Warn, "Failed to read Wi-fi settings from \"%s\"; using defaults instead\n", wfcsettingspath.c_str()); - firmware->AccessPoints() = { - WifiAccessPoint(Config::ConsoleType), - WifiAccessPoint(), - WifiAccessPoint(), + firmware->GetAccessPoints() = { + Firmware::WifiAccessPoint(Config::ConsoleType), + Firmware::WifiAccessPoint(), + Firmware::WifiAccessPoint(), }; - firmware->ExtendedAccessPoints() = { - ExtendedWifiAccessPoint(), - ExtendedWifiAccessPoint(), - ExtendedWifiAccessPoint(), + firmware->GetExtendedAccessPoints() = { + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), }; } @@ -862,71 +1087,107 @@ pair, string> GenerateDefaultFirmware() return std::make_pair(std::move(firmware), std::move(wfcsettingspath)); } -void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) +bool ParseMacAddress(void* data) { - using namespace SPI_Firmware; - UserData& currentData = firmware.EffectiveUserData(); + const std::string& mac_in = Config::FirmwareMAC; + u8* mac_out = (u8*)data; - // setting up username - std::string orig_username = Platform::GetConfigString(Platform::Firm_Username); - if (!orig_username.empty()) - { // If the frontend defines a username, take it. If not, leave the existing one. - std::u16string username = std::wstring_convert, char16_t>{}.from_bytes(orig_username); - size_t usernameLength = std::min(username.length(), (size_t) 10); - currentData.NameLength = usernameLength; - memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t)); - } - - auto language = static_cast(Platform::GetConfigInt(Platform::Firm_Language)); - if (language != Language::Reserved) - { // If the frontend specifies a language (rather than using the existing value)... - currentData.Settings &= ~Language::Reserved; // ..clear the existing language... - currentData.Settings |= language; // ...and set the new one. - } - - // setting up color - u8 favoritecolor = Platform::GetConfigInt(Platform::Firm_Color); - if (favoritecolor != 0xFF) + int o = 0; + u8 tmp = 0; + for (int i = 0; i < 18; i++) { - currentData.FavoriteColor = favoritecolor; + char c = mac_in[i]; + if (c == '\0') break; + + int n; + if (c >= '0' && c <= '9') n = c - '0'; + else if (c >= 'a' && c <= 'f') n = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') n = c - 'A' + 10; + else continue; + + if (!(o & 1)) + tmp = n; + else + mac_out[o >> 1] = n | (tmp << 4); + + o++; + if (o >= 12) return true; } - u8 birthmonth = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); - if (birthmonth != 0) - { // If the frontend specifies a birth month (rather than using the existing value)... - currentData.BirthdayMonth = birthmonth; - } + return false; +} - u8 birthday = Platform::GetConfigInt(Platform::Firm_BirthdayDay); - if (birthday != 0) - { // If the frontend specifies a birthday (rather than using the existing value)... - currentData.BirthdayDay = birthday; - } - - // setup message - std::string orig_message = Platform::GetConfigString(Platform::Firm_Message); - if (!orig_message.empty()) +void CustomizeFirmware(Firmware& firmware, bool overridesettings) noexcept +{ + if (overridesettings) { - std::u16string message = std::wstring_convert, char16_t>{}.from_bytes(orig_message); - size_t messageLength = std::min(message.length(), (size_t) 26); - currentData.MessageLength = messageLength; - memcpy(currentData.Message, message.data(), messageLength * sizeof(char16_t)); + auto ¤tData = firmware.GetEffectiveUserData(); + + // setting up username + std::string orig_username = Config::FirmwareUsername; + if (!orig_username.empty()) + { // If the frontend defines a username, take it. If not, leave the existing one. + std::u16string username = std::wstring_convert, char16_t>{}.from_bytes( + orig_username); + size_t usernameLength = std::min(username.length(), (size_t) 10); + currentData.NameLength = usernameLength; + memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t)); + } + + auto language = static_cast(Config::FirmwareLanguage); + if (language != Firmware::Language::Reserved) + { // If the frontend specifies a language (rather than using the existing value)... + currentData.Settings &= ~Firmware::Language::Reserved; // ..clear the existing language... + currentData.Settings |= language; // ...and set the new one. + } + + // setting up color + u8 favoritecolor = Config::FirmwareFavouriteColour; + if (favoritecolor != 0xFF) + { + currentData.FavoriteColor = favoritecolor; + } + + u8 birthmonth = Config::FirmwareBirthdayMonth; + if (birthmonth != 0) + { // If the frontend specifies a birth month (rather than using the existing value)... + currentData.BirthdayMonth = birthmonth; + } + + u8 birthday = Config::FirmwareBirthdayDay; + if (birthday != 0) + { // If the frontend specifies a birthday (rather than using the existing value)... + currentData.BirthdayDay = birthday; + } + + // setup message + std::string orig_message = Config::FirmwareMessage; + if (!orig_message.empty()) + { + std::u16string message = std::wstring_convert, char16_t>{}.from_bytes( + orig_message); + size_t messageLength = std::min(message.length(), (size_t) 26); + currentData.MessageLength = messageLength; + memcpy(currentData.Message, message.data(), messageLength * sizeof(char16_t)); + } } MacAddress mac; bool rep = false; - auto& header = firmware.Header(); + auto& header = firmware.GetHeader(); - memcpy(&mac, header.MacAddress.data(), sizeof(MacAddress)); + memcpy(&mac, header.MacAddr.data(), sizeof(MacAddress)); - - MacAddress configuredMac; - rep = Platform::GetConfigArray(Platform::Firm_MAC, &configuredMac); - rep &= (configuredMac != MacAddress()); - - if (rep) + if (overridesettings) { - mac = configuredMac; + MacAddress configuredMac; + rep = ParseMacAddress(&configuredMac); + rep &= (configuredMac != MacAddress()); + + if (rep) + { + mac = configuredMac; + } } int inst = Platform::InstanceID(); @@ -941,61 +1202,19 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) if (rep) { mac[0] &= 0xFC; // ensure the MAC isn't a broadcast MAC - header.MacAddress = mac; + header.MacAddr = mac; header.UpdateChecksum(); } firmware.UpdateChecksums(); } -bool InstallFirmware() -{ - using namespace SPI_Firmware; - FirmwareSave.reset(); - unique_ptr firmware; - string firmwarepath; - bool generated = false; - - if (Config::ExternalBIOSEnable) - { // If we want to try loading a firmware dump... - - tie(firmware, firmwarepath) = LoadFirmwareFromFile(); - if (!firmware) - { // Try to load the configured firmware dump. If that fails... - Log(LogLevel::Warn, "Firmware not found! Generating default firmware.\n"); - } - } - - if (!firmware) - { // If we haven't yet loaded firmware (either because the load failed or we want to use the default...) - tie(firmware, firmwarepath) = GenerateDefaultFirmware(); - } - - if (!firmware) - return false; - - if (Config::FirmwareOverrideSettings) - { - LoadUserSettingsFromConfig(*firmware); - } - - FirmwareSave = std::make_unique(firmwarepath); - - return InstallFirmware(std::move(firmware)); -} - -bool LoadROM(QStringList filepath, bool reset) +// Loads ROM data without parsing it. Works for GBA and NDS ROMs. +bool LoadROMData(const QStringList& filepath, std::unique_ptr& filedata, u32& filelen, string& basepath, string& romname) noexcept { if (filepath.empty()) return false; - u8* filedata; - u32 filelen; - - std::string basepath; - std::string romname; - - int num = filepath.count(); - if (num == 1) + if (int num = filepath.count(); num == 1) { // regular file @@ -1007,38 +1226,35 @@ bool LoadROM(QStringList filepath, bool reset) if (len > 0x40000000) { Platform::CloseFile(f); - delete[] filedata; return false; } Platform::FileRewind(f); - filedata = new u8[len]; - size_t nread = Platform::FileRead(filedata, (size_t)len, 1, f); + filedata = make_unique(len); + size_t nread = Platform::FileRead(filedata.get(), (size_t)len, 1, f); + Platform::CloseFile(f); if (nread != 1) { - Platform::CloseFile(f); - delete[] filedata; + filedata = nullptr; return false; } - Platform::CloseFile(f); filelen = (u32)len; if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst") { - u8* outContent = nullptr; - u32 decompressed = DecompressROM(filedata, len, &outContent); + filelen = DecompressROM(filedata.get(), len, filedata); - if (decompressed > 0) + if (filelen > 0) { - delete[] filedata; - filedata = outContent; - filelen = decompressed; filename = filename.substr(0, filename.length() - 4); } else { - delete[] filedata; + filedata = nullptr; + filelen = 0; + basepath = ""; + romname = ""; return false; } } @@ -1046,19 +1262,21 @@ bool LoadROM(QStringList filepath, bool reset) int pos = LastSep(filename); if(pos != -1) basepath = filename.substr(0, pos); + romname = filename.substr(pos+1); + return true; } #ifdef ARCHIVE_SUPPORT_ENABLED else if (num == 2) { // file inside archive - s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); + s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), filedata, &filelen); if (lenread < 0) return false; if (!filedata) return false; if (lenread != filelen) { - delete[] filedata; + filedata = nullptr; return false; } @@ -1067,81 +1285,133 @@ bool LoadROM(QStringList filepath, bool reset) std::string std_romname = filepath.at(1).toStdString(); romname = std_romname.substr(LastSep(std_romname)+1); + return true; } #endif else return false; +} + +QString GetSavErrorString(std::string& filepath, bool gba) +{ + std::string console = gba ? "GBA" : "DS"; + std::string err1 = "Unable to write to "; + std::string err2 = " save.\nPlease check file/folder write permissions.\n\nAttempted to Access:\n"; + + err1 += console + err2 + filepath; + + return QString::fromStdString(err1); +} + +bool LoadROM(QMainWindow* mainWindow, EmuThread* emuthread, QStringList filepath, bool reset) +{ + unique_ptr filedata = nullptr; + u32 filelen; + std::string basepath; + std::string romname; + + if (!LoadROMData(filepath, filedata, filelen, basepath, romname)) + { + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); + return false; + } - if (NDSSave) delete NDSSave; NDSSave = nullptr; BaseROMDir = basepath; BaseROMName = romname; BaseAssetName = romname.substr(0, romname.rfind('.')); - if (!InstallFirmware()) - { - return false; - } - - if (reset) - { - NDS::SetConsoleType(Config::ConsoleType); - NDS::EjectCart(); - LoadBIOSFiles(); - NDS::Reset(); - SetBatteryLevels(); - } - u32 savelen = 0; - u8* savedata = nullptr; + std::unique_ptr savedata = nullptr; std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav"); std::string origsav = savname; savname += Platform::InstanceFileSuffix(); FileHandle* sav = Platform::OpenFile(savname, FileMode::Read); - if (!sav) sav = Platform::OpenFile(origsav, FileMode::Read); + if (!sav) + { + if (!Platform::CheckFileWritable(origsav)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(origsav, false)); + return false; + } + + sav = Platform::OpenFile(origsav, FileMode::Read); + } + else if (!Platform::CheckFileWritable(savname)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(savname, false)); + return false; + } + if (sav) { savelen = (u32)Platform::FileLength(sav); FileRewind(sav); - savedata = new u8[savelen]; - FileRead(savedata, savelen, 1, sav); + savedata = std::make_unique(savelen); + FileRead(savedata.get(), savelen, 1, sav); CloseFile(sav); } - bool res = NDS::LoadCart(filedata, filelen, savedata, savelen); - if (res && reset) + NDSCart::NDSCartArgs cartargs { + // Don't load the SD card itself yet, because we don't know if + // the ROM is homebrew or not. + // So this is the card we *would* load if the ROM were homebrew. + .SDCard = GetDLDISDCardArgs(), + .SRAM = std::move(savedata), + .SRAMLength = savelen, + }; + + auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs)); + if (!cart) { - if (Config::DirectBoot || NDS::NeedsDirectBoot()) + // If we couldn't parse the ROM... + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); + return false; + } + + if (reset) + { + if (!emuthread->UpdateConsole(std::move(cart), Keep {})) { - NDS::SetupDirectBoot(romname); + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); + return false; } - } - if (res) + InitFirmwareSaveManager(emuthread); + emuthread->NDS->Reset(); + + if (Config::DirectBoot || emuthread->NDS->NeedsDirectBoot()) + { // If direct boot is enabled or forced... + emuthread->NDS->SetupDirectBoot(romname); + } + + SetBatteryLevels(*emuthread->NDS); + SetDateTime(*emuthread->NDS); + } + else { - CartType = 0; - NDSSave = new SaveManager(savname); - - LoadCheats(); + assert(emuthread->NDS != nullptr); + emuthread->NDS->SetNDSCart(std::move(cart)); } - if (savedata) delete[] savedata; - delete[] filedata; - return res; + CartType = 0; + NDSSave = std::make_unique(savname); + LoadCheats(*emuthread->NDS); + + return true; // success } -void EjectCart() +void EjectCart(NDS& nds) { - if (NDSSave) delete NDSSave; NDSSave = nullptr; - UnloadCheats(); + UnloadCheats(nds); - NDS::EjectCart(); + nds.EjectCart(); CartType = -1; BaseROMDir = ""; @@ -1169,94 +1439,25 @@ QString CartLabel() } -bool LoadGBAROM(QStringList filepath) +bool LoadGBAROM(QMainWindow* mainWindow, NDS& nds, QStringList filepath) { - if (Config::ConsoleType == 1) return false; - if (filepath.empty()) return false; + if (nds.ConsoleType == 1) + { + QMessageBox::critical(mainWindow, "melonDS", "The DSi doesn't have a GBA slot."); + return false; + } - u8* filedata; + unique_ptr filedata = nullptr; u32 filelen; - std::string basepath; std::string romname; - int num = filepath.count(); - if (num == 1) + if (!LoadROMData(filepath, filedata, filelen, basepath, romname)) { - // regular file - - std::string filename = filepath.at(0).toStdString(); - FileHandle* f = Platform::OpenFile(filename, FileMode::Read); - if (!f) return false; - - long len = FileLength(f); - if (len > 0x40000000) - { - CloseFile(f); - return false; - } - - FileRewind(f); - filedata = new u8[len]; - size_t nread = FileRead(filedata, (size_t)len, 1, f); - if (nread != 1) - { - CloseFile(f); - delete[] filedata; - return false; - } - - CloseFile(f); - filelen = (u32)len; - - if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst") - { - u8* outContent = nullptr; - u32 decompressed = DecompressROM(filedata, len, &outContent); - - if (decompressed > 0) - { - delete[] filedata; - filedata = outContent; - filelen = decompressed; - filename = filename.substr(0, filename.length() - 4); - } - else - { - delete[] filedata; - return false; - } - } - - int pos = LastSep(filename); - basepath = filename.substr(0, pos); - romname = filename.substr(pos+1); - } -#ifdef ARCHIVE_SUPPORT_ENABLED - else if (num == 2) - { - // file inside archive - - u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); - if (lenread < 0) return false; - if (!filedata) return false; - if (lenread != filelen) - { - delete[] filedata; - return false; - } - - std::string std_archivepath = filepath.at(0).toStdString(); - basepath = std_archivepath.substr(0, LastSep(std_archivepath)); - - std::string std_romname = filepath.at(1).toStdString(); - romname = std_romname.substr(LastSep(std_romname)+1); - } -#endif - else + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); return false; + } - if (GBASave) delete GBASave; GBASave = nullptr; BaseGBAROMDir = basepath; @@ -1264,45 +1465,62 @@ bool LoadGBAROM(QStringList filepath) BaseGBAAssetName = romname.substr(0, romname.rfind('.')); u32 savelen = 0; - u8* savedata = nullptr; + std::unique_ptr savedata = nullptr; std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav"); std::string origsav = savname; savname += Platform::InstanceFileSuffix(); FileHandle* sav = Platform::OpenFile(savname, FileMode::Read); - if (!sav) sav = Platform::OpenFile(origsav, FileMode::Read); + if (!sav) + { + if (!Platform::CheckFileWritable(origsav)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(origsav, true)); + return false; + } + + sav = Platform::OpenFile(origsav, FileMode::Read); + } + else if (!Platform::CheckFileWritable(savname)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(savname, true)); + return false; + } + if (sav) { savelen = (u32)FileLength(sav); - FileRewind(sav); - savedata = new u8[savelen]; - FileRead(savedata, savelen, 1, sav); + if (savelen > 0) + { + FileRewind(sav); + savedata = std::make_unique(savelen); + FileRead(savedata.get(), savelen, 1, sav); + } CloseFile(sav); } - bool res = NDS::LoadGBACart(filedata, filelen, savedata, savelen); - - if (res) + auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen); + if (!cart) { - GBACartType = 0; - GBASave = new SaveManager(savname); + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); + return false; } - if (savedata) delete[] savedata; - delete[] filedata; - return res; + nds.SetGBACart(std::move(cart)); + GBACartType = 0; + GBASave = std::make_unique(savname); + return true; } -void LoadGBAAddon(int type) +void LoadGBAAddon(NDS& nds, int type) { if (Config::ConsoleType == 1) return; - if (GBASave) delete GBASave; GBASave = nullptr; - NDS::LoadGBAAddon(type); + nds.LoadGBAAddon(type); GBACartType = type; BaseGBAROMDir = ""; @@ -1310,12 +1528,11 @@ void LoadGBAAddon(int type) BaseGBAAssetName = ""; } -void EjectGBACart() +void EjectGBACart(NDS& nds) { - if (GBASave) delete GBASave; GBASave = nullptr; - NDS::EjectGBACart(); + nds.EjectGBACart(); GBACartType = -1; BaseGBAROMDir = ""; @@ -1345,7 +1562,7 @@ QString GBACartLabel() return ret; } - case NDS::GBAAddon_RAMExpansion: + case GBAAddon_RAMExpansion: return "Memory expansion"; } @@ -1353,25 +1570,30 @@ QString GBACartLabel() } -void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef) +void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32 (&iconRef)[32*32]) { - int index = 0; - for (int i = 0; i < 4; i++) + u32 paletteRGBA[16]; + for (int i = 0; i < 16; i++) { - for (int j = 0; j < 4; j++) + u8 r = ((palette[i] >> 0) & 0x1F) * 255 / 31; + u8 g = ((palette[i] >> 5) & 0x1F) * 255 / 31; + u8 b = ((palette[i] >> 10) & 0x1F) * 255 / 31; + u8 a = i ? 255 : 0; + paletteRGBA[i] = r | (g << 8) | (b << 16) | (a << 24); + } + + int count = 0; + for (int ytile = 0; ytile < 4; ytile++) + { + for (int xtile = 0; xtile < 4; xtile++) { - for (int k = 0; k < 8; k++) + for (int ypixel = 0; ypixel < 8; ypixel++) { - for (int l = 0; l < 8; l++) + for (int xpixel = 0; xpixel < 8; xpixel++) { - u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F; - u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31; - u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31; - u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31; - u8 a = pal_index ? 255: 0; - u32* row = &iconRef[256 * i + 32 * k + 8 * j]; - row[l] = (a << 24) | (r << 16) | (g << 8) | b; - index++; + u8 pal_index = count % 2 ? data[count/2] >> 4 : data[count/2] & 0x0F; + iconRef[ytile*256 + ypixel*32 + xtile*8 + xpixel] = paletteRGBA[pal_index]; + count++; } } } @@ -1384,14 +1606,15 @@ void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef) #define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8) #define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0) -void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], const u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector &animatedSequenceRef) +void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], const u16 (&sequence)[64], u32 (&animatedIconRef)[64][32*32], std::vector &animatedSequenceRef) { for (int i = 0; i < 64; i++) { if (!sequence[i]) break; - u32* frame = &animatedTexRef[32 * 32 * i]; - ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame); + + ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], animatedIconRef[i]); + u32* frame = animatedIconRef[i]; if (SEQ_FLIPH(sequence[i])) { diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 5faef1a8..ae854617 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,49 +22,83 @@ #include "types.h" #include "SaveManager.h" #include "AREngine.h" +#include "DSi_NAND.h" +#include +#include "MemConstants.h" + +#include +#include #include #include #include +namespace melonDS +{ +class NDS; +class DSi; +class FATStorage; +class FATStorageArgs; +} +class EmuThread; namespace ROMManager { -extern SaveManager* NDSSave; -extern SaveManager* GBASave; +using namespace melonDS; +extern std::unique_ptr NDSSave; +extern std::unique_ptr GBASave; extern std::unique_ptr FirmwareSave; QString VerifySetup(); -void Reset(); -bool LoadBIOS(); +void Reset(EmuThread* thread); + +/// Boots the emulated console into its system menu without starting a game. +bool BootToMenu(EmuThread* thread); void ClearBackupState(); -bool InstallFirmware(); -bool LoadROM(QStringList filepath, bool reset); -void EjectCart(); +/// Returns the configured ARM9 BIOS loaded from disk, +/// the FreeBIOS if external BIOS is disabled and we're in NDS mode, +/// or nullptr if loading failed. +std::unique_ptr LoadARM9BIOS() noexcept; +std::unique_ptr LoadARM7BIOS() noexcept; +std::unique_ptr LoadDSiARM9BIOS() noexcept; +std::unique_ptr LoadDSiARM7BIOS() noexcept; +std::optional GetDSiSDCardArgs() noexcept; +std::optional LoadDSiSDCard() noexcept; +std::optional GetDLDISDCardArgs() noexcept; +std::optional LoadDLDISDCard() noexcept; +void CustomizeFirmware(Firmware& firmware, bool overridesettings) noexcept; +Firmware GenerateFirmware(int type) noexcept; +/// Loads and customizes a firmware image based on the values in Config +std::optional LoadFirmware(int type) noexcept; +/// Loads and customizes a NAND image based on the values in Config +std::optional LoadNAND(const std::array& arm7ibios) noexcept; + +/// Inserts a ROM into the emulated console. +bool LoadROM(QMainWindow* mainWindow, EmuThread*, QStringList filepath, bool reset); +void EjectCart(NDS& nds); bool CartInserted(); QString CartLabel(); -bool LoadGBAROM(QStringList filepath); -void LoadGBAAddon(int type); -void EjectGBACart(); +bool LoadGBAROM(QMainWindow* mainWindow, NDS& nds, QStringList filepath); +void LoadGBAAddon(NDS& nds, int type); +void EjectGBACart(NDS& nds); bool GBACartInserted(); QString GBACartLabel(); std::string GetSavestateName(int slot); bool SavestateExists(int slot); -bool LoadState(const std::string& filename); -bool SaveState(const std::string& filename); -void UndoStateLoad(); +bool LoadState(NDS& nds, const std::string& filename); +bool SaveState(NDS& nds, const std::string& filename); +void UndoStateLoad(NDS& nds); -void EnableCheats(bool enable); +void EnableCheats(NDS& nds, bool enable); ARCodeFile* GetCheatFile(); -void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef); +void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32 (&iconRef)[32*32]); void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], - const u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], + const u16 (&sequence)[64], u32 (&animatedIconRef)[64][32*32], std::vector &animatedSequenceRef); - } #endif // ROMMANAGER_H diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp index ee550912..55279dca 100644 --- a/src/frontend/qt_sdl/SaveManager.cpp +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,7 +22,8 @@ #include "SaveManager.h" #include "Platform.h" -using namespace Platform; +using namespace melonDS; +using namespace melonDS::Platform; SaveManager::SaveManager(const std::string& path) : QThread() { diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h index 3d85af66..d7132e69 100644 --- a/src/frontend/qt_sdl/SaveManager.h +++ b/src/frontend/qt_sdl/SaveManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -41,31 +41,31 @@ public: std::string GetPath(); void SetPath(const std::string& path, bool reload); - void RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); + void RequestFlush(const melonDS::u8* savedata, melonDS::u32 savelen, melonDS::u32 writeoffset, melonDS::u32 writelen); void CheckFlush(); bool NeedsFlush(); - void FlushSecondaryBuffer(u8* dst = nullptr, u32 dstLength = 0); + void FlushSecondaryBuffer(melonDS::u8* dst = nullptr, melonDS::u32 dstLength = 0); private: std::string Path; std::atomic_bool Running; - std::unique_ptr Buffer; - u32 Length; + std::unique_ptr Buffer; + melonDS::u32 Length; bool FlushRequested; QMutex* SecondaryBufferLock; - std::unique_ptr SecondaryBuffer; - u32 SecondaryBufferLength; + std::unique_ptr SecondaryBuffer; + melonDS::u32 SecondaryBufferLength; time_t TimeAtLastFlushRequest; // We keep versions in case the user closes the application before // a flush cycle is finished. - u32 PreviousFlushVersion; - u32 FlushVersion; + melonDS::u32 PreviousFlushVersion; + melonDS::u32 FlushVersion; }; #endif // SAVEMANAGER_H diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp new file mode 100644 index 00000000..73236504 --- /dev/null +++ b/src/frontend/qt_sdl/Screen.cpp @@ -0,0 +1,1072 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#ifndef _WIN32 +#ifndef APPLE +#include +#endif +#endif +#include + +#include "OpenGLSupport.h" +#include "duckstation/gl/context.h" + +#include "main.h" + +#include "NDS.h" +#include "GPU.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" +#include "Platform.h" +#include "Config.h" + +#include "main_shaders.h" +#include "OSD_shaders.h" +#include "font.h" + +using namespace melonDS; + + +// TEMP +extern MainWindow* mainWindow; +extern EmuThread* emuThread; +extern bool RunningSomething; +extern int autoScreenSizing; + +extern int videoRenderer; +extern bool videoSettingsDirty; + +const u32 kOSDMargin = 6; + + +ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent) +{ + setMouseTracking(true); + setAttribute(Qt::WA_AcceptTouchEvents); + QTimer* mouseTimer = setupMouseTimer(); + connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) setCursor(Qt::BlankCursor);}); + + osdEnabled = false; + osdID = 1; +} + +ScreenPanel::~ScreenPanel() +{ + mouseTimer->stop(); + delete mouseTimer; +} + +void ScreenPanel::setupScreenLayout() +{ + int w = width(); + int h = height(); + + int sizing = Config::ScreenSizing; + if (sizing == 3) sizing = autoScreenSizing; + + float aspectTop, aspectBot; + + for (auto ratio : aspectRatios) + { + if (ratio.id == Config::ScreenAspectTop) + aspectTop = ratio.ratio; + if (ratio.id == Config::ScreenAspectBot) + aspectBot = ratio.ratio; + } + + if (aspectTop == 0) + aspectTop = ((float) w / h) / (4.f / 3.f); + + if (aspectBot == 0) + aspectBot = ((float) w / h) / (4.f / 3.f); + + Frontend::SetupScreenLayout(w, h, + static_cast(Config::ScreenLayout), + static_cast(Config::ScreenRotation), + static_cast(sizing), + Config::ScreenGap, + Config::IntegerScaling != 0, + Config::ScreenSwap != 0, + aspectTop, + aspectBot); + + numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); +} + +QSize ScreenPanel::screenGetMinSize(int factor = 1) +{ + bool isHori = (Config::ScreenRotation == Frontend::screenRot_90Deg + || Config::ScreenRotation == Frontend::screenRot_270Deg); + int gap = Config::ScreenGap * factor; + + int w = 256 * factor; + int h = 192 * factor; + + if (Config::ScreenSizing == Frontend::screenSizing_TopOnly + || Config::ScreenSizing == Frontend::screenSizing_BotOnly) + { + return QSize(w, h); + } + + if (Config::ScreenLayout == Frontend::screenLayout_Natural) + { + if (isHori) + return QSize(h+gap+h, w); + else + return QSize(w, h+gap+h); + } + else if (Config::ScreenLayout == Frontend::screenLayout_Vertical) + { + if (isHori) + return QSize(h, w+gap+w); + else + return QSize(w, h+gap+h); + } + else if (Config::ScreenLayout == Frontend::screenLayout_Horizontal) + { + if (isHori) + return QSize(h+gap+h, w); + else + return QSize(w+gap+w, h); + } + else // hybrid + { + if (isHori) + return QSize(h+gap+h, 3*w + (int)ceil((4*gap) / 3.0)); + else + return QSize(3*w + (int)ceil((4*gap) / 3.0), h+gap+h); + } +} + +void ScreenPanel::onScreenLayoutChanged() +{ + setMinimumSize(screenGetMinSize()); + setupScreenLayout(); +} + +void ScreenPanel::resizeEvent(QResizeEvent* event) +{ + setupScreenLayout(); + QWidget::resizeEvent(event); +} + +void ScreenPanel::mousePressEvent(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + if (Frontend::GetTouchCoords(x, y, false)) + { + touching = true; + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } +} + +void ScreenPanel::mouseReleaseEvent(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + if (touching) + { + touching = false; + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); + } +} + +void ScreenPanel::mouseMoveEvent(QMouseEvent* event) +{ + event->accept(); + + showCursor(); + + //if (!(event->buttons() & Qt::LeftButton)) return; + if (!touching) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + if (Frontend::GetTouchCoords(x, y, true)) + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } +} + +void ScreenPanel::tabletEvent(QTabletEvent* event) +{ + event->accept(); + + switch(event->type()) + { + case QEvent::TabletPress: + case QEvent::TabletMove: + { + int x = event->x(); + int y = event->y(); + + if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) + { + touching = true; + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } + } + break; + case QEvent::TabletRelease: + if (touching) + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); + touching = false; + } + break; + default: + break; + } +} + +void ScreenPanel::touchEvent(QTouchEvent* event) +{ + event->accept(); + + switch(event->type()) + { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + if (event->touchPoints().length() > 0) + { + QPointF lastPosition = event->touchPoints().first().lastPos(); + int x = (int)lastPosition.x(); + int y = (int)lastPosition.y(); + + if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) + { + touching = true; + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } + } + break; + case QEvent::TouchEnd: + if (touching) + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); + touching = false; + } + break; + default: + break; + } +} + +bool ScreenPanel::event(QEvent* event) +{ + if (event->type() == QEvent::TouchBegin + || event->type() == QEvent::TouchEnd + || event->type() == QEvent::TouchUpdate) + { + touchEvent((QTouchEvent*)event); + return true; + } + + return QWidget::event(event); +} + +void ScreenPanel::showCursor() +{ + mainWindow->panel->setCursor(Qt::ArrowCursor); + mouseTimer->start(); +} + +QTimer* ScreenPanel::setupMouseTimer() +{ + mouseTimer = new QTimer(); + mouseTimer->setSingleShot(true); + mouseTimer->setInterval(Config::MouseHideSeconds*1000); + mouseTimer->start(); + + return mouseTimer; +} + +int ScreenPanel::osdFindBreakPoint(const char* text, int i) +{ + // i = character that went out of bounds + + for (int j = i; j >= 0; j--) + { + if (text[j] == ' ') + return j; + } + + return i; +} + +void ScreenPanel::osdLayoutText(const char* text, int* width, int* height, int* breaks) +{ + int w = 0; + int h = 14; + int totalw = 0; + int maxw = ((QWidget*)this)->width() - (kOSDMargin*2); + int lastbreak = -1; + int numbrk = 0; + u16* ptr; + + memset(breaks, 0, sizeof(int)*64); + + for (int i = 0; text[i] != '\0'; ) + { + int glyphsize; + if (text[i] == ' ') + { + glyphsize = 6; + } + else + { + u32 ch = text[i]; + if (ch < 0x10 || ch > 0x7E) ch = 0x7F; + + ptr = &::font[(ch-0x10) << 4]; + glyphsize = ptr[0]; + if (!glyphsize) glyphsize = 6; + else glyphsize += 2; // space around the character + } + + w += glyphsize; + if (w > maxw) + { + // wrap shit as needed + if (text[i] == ' ') + { + if (numbrk >= 64) break; + breaks[numbrk++] = i; + i++; + } + else + { + int brk = osdFindBreakPoint(text, i); + if (brk != lastbreak) i = brk; + + if (numbrk >= 64) break; + breaks[numbrk++] = i; + + lastbreak = brk; + } + + w = 0; + h += 14; + } + else + i++; + + if (w > totalw) totalw = w; + } + + *width = totalw; + *height = h; +} + +unsigned int ScreenPanel::osdRainbowColor(int inc) +{ + // inspired from Acmlmboard + + if (inc < 100) return 0xFFFF9B9B + (inc << 8); + else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16); + else if (inc < 300) return 0xFF9BFF9B + (inc-200); + else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8); + else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16); + else return 0xFFFF9BFF - (inc-500); +} + +void ScreenPanel::osdRenderItem(OSDItem* item) +{ + int w, h; + int breaks[64]; + + char* text = item->text; + u32 color = item->color; + + bool rainbow = (color == 0); + u32 ticks = (u32)QDateTime::currentMSecsSinceEpoch(); + u32 rainbowinc = ((text[0] * 17) + (ticks * 13)) % 600; + + color |= 0xFF000000; + const u32 shadow = 0xE0000000; + + osdLayoutText(text, &w, &h, breaks); + + item->bitmap = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + u32* bitmap = (u32*)item->bitmap.bits(); + memset(bitmap, 0, w*h*sizeof(u32)); + + int x = 0, y = 1; + u32 maxw = ((QWidget*)this)->width() - (kOSDMargin*2); + int curline = 0; + u16* ptr; + + for (int i = 0; text[i] != '\0'; ) + { + int glyphsize; + if (text[i] == ' ') + { + x += 6; + } + else + { + u32 ch = text[i]; + if (ch < 0x10 || ch > 0x7E) ch = 0x7F; + + ptr = &::font[(ch-0x10) << 4]; + int glyphsize = ptr[0]; + if (!glyphsize) x += 6; + else + { + x++; + + if (rainbow) + { + color = osdRainbowColor(rainbowinc); + rainbowinc = (rainbowinc + 30) % 600; + } + + // draw character + for (int cy = 0; cy < 12; cy++) + { + u16 val = ptr[4+cy]; + + for (int cx = 0; cx < glyphsize; cx++) + { + if (val & (1<= breaks[curline]) + { + i = breaks[curline++]; + if (text[i] == ' ') i++; + + x = 0; + y += 14; + } + } + + // shadow + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + u32 val; + + val = bitmap[(y * w) + x]; + if ((val >> 24) == 0xFF) continue; + + if (x > 0) val = bitmap[(y * w) + x-1]; + if (x < w-1) val |= bitmap[(y * w) + x+1]; + if (y > 0) + { + if (x > 0) val |= bitmap[((y-1) * w) + x-1]; + val |= bitmap[((y-1) * w) + x]; + if (x < w-1) val |= bitmap[((y-1) * w) + x+1]; + } + if (y < h-1) + { + if (x > 0) val |= bitmap[((y+1) * w) + x-1]; + val |= bitmap[((y+1) * w) + x]; + if (x < w-1) val |= bitmap[((y+1) * w) + x+1]; + } + + if ((val >> 24) == 0xFF) + bitmap[(y * w) + x] = shadow; + } + } +} + +void ScreenPanel::osdDeleteItem(OSDItem* item) +{ +} + +void ScreenPanel::osdSetEnabled(bool enabled) +{ + osdMutex.lock(); + osdEnabled = enabled; + osdMutex.unlock(); +} + +void ScreenPanel::osdAddMessage(unsigned int color, const char* text) +{ + if (!osdEnabled) return; + + osdMutex.lock(); + + OSDItem item; + + item.id = osdID++; + item.timestamp = QDateTime::currentMSecsSinceEpoch(); + strncpy(item.text, text, 255); item.text[255] = '\0'; + item.color = color; + item.rendered = false; + + osdItems.push_back(item); + + osdMutex.unlock(); +} + +void ScreenPanel::osdUpdate() +{ + osdMutex.lock(); + + qint64 tick_now = QDateTime::currentMSecsSinceEpoch(); + qint64 tick_min = tick_now - 2500; + + for (auto it = osdItems.begin(); it != osdItems.end(); ) + { + OSDItem& item = *it; + + if ((!osdEnabled) || (item.timestamp < tick_min)) + { + osdDeleteItem(&item); + it = osdItems.erase(it); + continue; + } + + if (!item.rendered) + { + osdRenderItem(&item); + item.rendered = true; + } + + it++; + } + + osdMutex.unlock(); +} + + + +ScreenPanelNative::ScreenPanelNative(QWidget* parent) : ScreenPanel(parent) +{ + screen[0] = QImage(256, 192, QImage::Format_RGB32); + screen[1] = QImage(256, 192, QImage::Format_RGB32); + + screenTrans[0].reset(); + screenTrans[1].reset(); +} + +ScreenPanelNative::~ScreenPanelNative() +{ +} + +void ScreenPanelNative::setupScreenLayout() +{ + ScreenPanel::setupScreenLayout(); + + for (int i = 0; i < numScreens; i++) + { + float* mtx = screenMatrix[i]; + screenTrans[i].setMatrix(mtx[0], mtx[1], 0.f, + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); + } +} + +void ScreenPanelNative::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + + // fill background + painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); + + if (emuThread->emuIsActive()) + { + assert(emuThread->NDS != nullptr); + emuThread->FrontBufferLock.lock(); + int frontbuf = emuThread->FrontBuffer; + if (!emuThread->NDS->GPU.Framebuffer[frontbuf][0] || !emuThread->NDS->GPU.Framebuffer[frontbuf][1]) + { + emuThread->FrontBufferLock.unlock(); + return; + } + + memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4); + memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4); + emuThread->FrontBufferLock.unlock(); + + QRect screenrc(0, 0, 256, 192); + + for (int i = 0; i < numScreens; i++) + { + painter.setTransform(screenTrans[i]); + painter.drawImage(screenrc, screen[screenKind[i]]); + } + } + + osdUpdate(); + if (osdEnabled) + { + osdMutex.lock(); + + u32 y = kOSDMargin; + + painter.resetTransform(); + + for (auto it = osdItems.begin(); it != osdItems.end(); ) + { + OSDItem& item = *it; + + painter.drawImage(kOSDMargin, y, item.bitmap); + + y += item.bitmap.height(); + it++; + } + + osdMutex.unlock(); + } +} + + + +ScreenPanelGL::ScreenPanelGL(QWidget* parent) : ScreenPanel(parent) +{ + setAutoFillBackground(false); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_KeyCompression, false); + setFocusPolicy(Qt::StrongFocus); + setMinimumSize(screenGetMinSize()); +} + +ScreenPanelGL::~ScreenPanelGL() +{} + +bool ScreenPanelGL::createContext() +{ + std::optional windowInfo = getWindowInfo(); + std::array versionsToTry = { + GL::Context::Version{GL::Context::Profile::Core, 4, 3}, + GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; + if (windowInfo.has_value()) + { + glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); + glContext->DoneCurrent(); + } + + return glContext != nullptr; +} + +void ScreenPanelGL::setSwapInterval(int intv) +{ + if (!glContext) return; + + glContext->SetSwapInterval(intv); +} + +void ScreenPanelGL::initOpenGL() +{ + if (!glContext) return; + + glContext->MakeCurrent(); + + OpenGL::BuildShaderProgram(kScreenVS, kScreenFS, screenShaderProgram, "ScreenShader"); + GLuint pid = screenShaderProgram[2]; + glBindAttribLocation(pid, 0, "vPosition"); + glBindAttribLocation(pid, 1, "vTexcoord"); + glBindFragDataLocation(pid, 0, "oColor"); + + OpenGL::LinkShaderProgram(screenShaderProgram); + + glUseProgram(pid); + glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0); + + screenShaderScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); + screenShaderTransformULoc = glGetUniformLocation(pid, "uTransform"); + + // to prevent bleeding between both parts of the screen + // with bilinear filtering enabled + const int paddedHeight = 192*2+2; + const float padPixels = 1.f / paddedHeight; + + const float vertices[] = + { + 0.f, 0.f, 0.f, 0.f, + 0.f, 192.f, 0.f, 0.5f - padPixels, + 256.f, 192.f, 1.f, 0.5f - padPixels, + 0.f, 0.f, 0.f, 0.f, + 256.f, 192.f, 1.f, 0.5f - padPixels, + 256.f, 0.f, 1.f, 0.f, + + 0.f, 0.f, 0.f, 0.5f + padPixels, + 0.f, 192.f, 0.f, 1.f, + 256.f, 192.f, 1.f, 1.f, + 0.f, 0.f, 0.f, 0.5f + padPixels, + 256.f, 192.f, 1.f, 1.f, + 256.f, 0.f, 1.f, 0.5f + padPixels + }; + + glGenBuffers(1, &screenVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &screenVertexArray); + glBindVertexArray(screenVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); + glEnableVertexAttribArray(1); // texcoord + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); + + glGenTextures(1, &screenTexture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screenTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + // fill the padding + u8 zeroData[256*4*4]; + memset(zeroData, 0, sizeof(zeroData)); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); + + + OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, osdShader, "OSDShader"); + + pid = osdShader[2]; + glBindAttribLocation(pid, 0, "vPosition"); + glBindFragDataLocation(pid, 0, "oColor"); + + OpenGL::LinkShaderProgram(osdShader); + glUseProgram(pid); + glUniform1i(glGetUniformLocation(pid, "OSDTex"), 0); + + osdScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); + osdPosULoc = glGetUniformLocation(pid, "uOSDPos"); + osdSizeULoc = glGetUniformLocation(pid, "uOSDSize"); + osdScaleFactorULoc = glGetUniformLocation(pid, "uScaleFactor"); + + const float osdvertices[6*2] = + { + 0, 0, + 1, 1, + 1, 0, + 0, 0, + 0, 1, + 1, 1 + }; + + glGenBuffers(1, &osdVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(osdvertices), osdvertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &osdVertexArray); + glBindVertexArray(osdVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); + + + glContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); + transferLayout(); +} + +void ScreenPanelGL::deinitOpenGL() +{ + if (!glContext) return; + + glDeleteTextures(1, &screenTexture); + + glDeleteVertexArrays(1, &screenVertexArray); + glDeleteBuffers(1, &screenVertexBuffer); + + OpenGL::DeleteShaderProgram(screenShaderProgram); + + + for (const auto& [key, tex] : osdTextures) + { + glDeleteTextures(1, &tex); + } + osdTextures.clear(); + + glDeleteVertexArrays(1, &osdVertexArray); + glDeleteBuffers(1, &osdVertexBuffer); + + OpenGL::DeleteShaderProgram(osdShader); + + + glContext->DoneCurrent(); + + lastScreenWidth = lastScreenHeight = -1; +} + +void ScreenPanelGL::osdRenderItem(OSDItem* item) +{ + ScreenPanel::osdRenderItem(item); + + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item->bitmap.width(), item->bitmap.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, item->bitmap.bits()); + + osdTextures[item->id] = tex; +} + +void ScreenPanelGL::osdDeleteItem(OSDItem* item) +{ + if (osdTextures.count(item->id)) + { + GLuint tex = osdTextures[item->id]; + glDeleteTextures(1, &tex); + osdTextures.erase(item->id); + } + + ScreenPanel::osdDeleteItem(item); +} + +void ScreenPanelGL::drawScreenGL() +{ + if (!glContext) return; + if (!emuThread->NDS) return; + + int w = windowInfo.surface_width; + int h = windowInfo.surface_height; + float factor = windowInfo.surface_scale; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, w, h); + + glUseProgram(screenShaderProgram[2]); + glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); + + int frontbuf = emuThread->FrontBuffer; + glActiveTexture(GL_TEXTURE0); + +#ifdef OGLRENDERER_ENABLED + if (emuThread->NDS->GPU.GetRenderer3D().Accelerated) + { + // hardware-accelerated render + static_cast(emuThread->NDS->GPU.GetRenderer3D()).GetCompositor().BindOutputTexture(frontbuf); + } + else +#endif + { + // regular render + glBindTexture(GL_TEXTURE_2D, screenTexture); + + if (emuThread->NDS->GPU.Framebuffer[frontbuf][0] && emuThread->NDS->GPU.Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, emuThread->NDS->GPU.Framebuffer[frontbuf][0].get()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, emuThread->NDS->GPU.Framebuffer[frontbuf][1].get()); + } + } + + screenSettingsLock.lock(); + + GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBindVertexArray(screenVertexArray); + + for (int i = 0; i < numScreens; i++) + { + glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); + glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); + } + + screenSettingsLock.unlock(); + + osdUpdate(); + if (osdEnabled) + { + osdMutex.lock(); + + u32 y = kOSDMargin; + + glUseProgram(osdShader[2]); + + glUniform2f(osdScreenSizeULoc, w, h); + glUniform1f(osdScaleFactorULoc, factor); + + glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer); + glBindVertexArray(osdVertexArray); + + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + for (auto it = osdItems.begin(); it != osdItems.end(); ) + { + OSDItem& item = *it; + + if (!osdTextures.count(item.id)) + continue; + + glBindTexture(GL_TEXTURE_2D, osdTextures[item.id]); + glUniform2i(osdPosULoc, kOSDMargin, y); + glUniform2i(osdSizeULoc, item.bitmap.width(), item.bitmap.height()); + glDrawArrays(GL_TRIANGLES, 0, 2*3); + + y += item.bitmap.height(); + it++; + } + + glDisable(GL_BLEND); + glUseProgram(0); + + osdMutex.unlock(); + } + + glContext->SwapBuffers(); +} + +qreal ScreenPanelGL::devicePixelRatioFromScreen() const +{ + const QScreen* screen_for_ratio = window()->windowHandle()->screen(); + if (!screen_for_ratio) + screen_for_ratio = QGuiApplication::primaryScreen(); + + return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); +} + +int ScreenPanelGL::scaledWindowWidth() const +{ + return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); +} + +int ScreenPanelGL::scaledWindowHeight() const +{ + return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); +} + +std::optional ScreenPanelGL::getWindowInfo() +{ + WindowInfo wi; + + // Windows and Apple are easy here since there's no display connection. + #if defined(_WIN32) + wi.type = WindowInfo::Type::Win32; + wi.window_handle = reinterpret_cast(winId()); + #elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast(winId()); + #else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("xcb")) + { + wi.type = WindowInfo::Type::X11; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = reinterpret_cast(winId()); + } + else if (platform_name == QStringLiteral("wayland")) + { + wi.type = WindowInfo::Type::Wayland; + QWindow* handle = windowHandle(); + if (handle == nullptr) + return std::nullopt; + + wi.display_connection = pni->nativeResourceForWindow("display", handle); + wi.window_handle = pni->nativeResourceForWindow("surface", handle); + } + else + { + qCritical() << "Unknown PNI platform " << platform_name; + return std::nullopt; + } + #endif + + wi.surface_width = static_cast(scaledWindowWidth()); + wi.surface_height = static_cast(scaledWindowHeight()); + wi.surface_scale = static_cast(devicePixelRatioFromScreen()); + + return wi; +} + + +QPaintEngine* ScreenPanelGL::paintEngine() const +{ + return nullptr; +} + +void ScreenPanelGL::setupScreenLayout() +{ + ScreenPanel::setupScreenLayout(); + transferLayout(); +} + +void ScreenPanelGL::transferLayout() +{ + std::optional windowInfo = getWindowInfo(); + if (windowInfo.has_value()) + { + screenSettingsLock.lock(); + + if (lastScreenWidth != windowInfo->surface_width || lastScreenHeight != windowInfo->surface_height) + { + if (glContext) + glContext->ResizeSurface(windowInfo->surface_width, windowInfo->surface_height); + lastScreenWidth = windowInfo->surface_width; + lastScreenHeight = windowInfo->surface_height; + } + + this->filter = Config::ScreenFilter; + this->windowInfo = *windowInfo; + + screenSettingsLock.unlock(); + } +} diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h new file mode 100644 index 00000000..c2f7fda1 --- /dev/null +++ b/src/frontend/qt_sdl/Screen.h @@ -0,0 +1,196 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef SCREEN_H +#define SCREEN_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "glad/glad.h" +#include "FrontendUtil.h" +#include "duckstation/gl/context.h" + + +class EmuThread; + + +const struct { int id; float ratio; const char* label; } aspectRatios[] = +{ + { 0, 1, "4:3 (native)" }, + { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, + { 1, (16.f / 9) / (4.f / 3), "16:9" }, + { 2, (21.f / 9) / (4.f / 3), "21:9" }, + { 3, 0, "window" } +}; +constexpr int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]); + + +class ScreenPanel : public QWidget +{ + Q_OBJECT + +public: + explicit ScreenPanel(QWidget* parent); + virtual ~ScreenPanel(); + + QTimer* setupMouseTimer(); + void updateMouseTimer(); + QTimer* mouseTimer; + QSize screenGetMinSize(int factor); + + void osdSetEnabled(bool enabled); + void osdAddMessage(unsigned int color, const char* msg); + +private slots: + void onScreenLayoutChanged(); + +protected: + struct OSDItem + { + unsigned int id; + qint64 timestamp; + + char text[256]; + unsigned int color; + + bool rendered; + QImage bitmap; + }; + + QMutex osdMutex; + bool osdEnabled; + unsigned int osdID; + std::deque osdItems; + + virtual void setupScreenLayout(); + + void resizeEvent(QResizeEvent* event) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + void tabletEvent(QTabletEvent* event) override; + void touchEvent(QTouchEvent* event); + bool event(QEvent* event) override; + + float screenMatrix[Frontend::MaxScreenTransforms][6]; + int screenKind[Frontend::MaxScreenTransforms]; + int numScreens; + + bool touching = false; + + void showCursor(); + + int osdFindBreakPoint(const char* text, int i); + void osdLayoutText(const char* text, int* width, int* height, int* breaks); + unsigned int osdRainbowColor(int inc); + + virtual void osdRenderItem(OSDItem* item); + virtual void osdDeleteItem(OSDItem* item); + + void osdUpdate(); +}; + + +class ScreenPanelNative : public ScreenPanel +{ + Q_OBJECT + +public: + explicit ScreenPanelNative(QWidget* parent); + virtual ~ScreenPanelNative(); + +protected: + void paintEvent(QPaintEvent* event) override; + +private: + void setupScreenLayout() override; + + QImage screen[2]; + QTransform screenTrans[Frontend::MaxScreenTransforms]; +}; + + +class ScreenPanelGL : public ScreenPanel +{ + Q_OBJECT + +public: + explicit ScreenPanelGL(QWidget* parent); + virtual ~ScreenPanelGL(); + + std::optional getWindowInfo(); + + bool createContext(); + + void setSwapInterval(int intv); + + void initOpenGL(); + void deinitOpenGL(); + void drawScreenGL(); + + GL::Context* getContext() { return glContext.get(); } + + void transferLayout(); +protected: + + qreal devicePixelRatioFromScreen() const; + int scaledWindowWidth() const; + int scaledWindowHeight() const; + + QPaintEngine* paintEngine() const override; + +private: + void setupScreenLayout() override; + + std::unique_ptr glContext; + + GLuint screenVertexBuffer, screenVertexArray; + GLuint screenTexture; + GLuint screenShaderProgram[3]; + GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; + + QMutex screenSettingsLock; + WindowInfo windowInfo; + bool filter; + + int lastScreenWidth = -1, lastScreenHeight = -1; + + GLuint osdShader[3]; + GLint osdScreenSizeULoc, osdPosULoc, osdSizeULoc; + GLfloat osdScaleFactorULoc; + GLuint osdVertexArray; + GLuint osdVertexBuffer; + std::map osdTextures; + + void osdRenderItem(OSDItem* item) override; + void osdDeleteItem(OSDItem* item) override; +}; + +#endif // SCREEN_H + diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index d5147fc6..21ca844e 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -30,15 +30,16 @@ #include "ui_TitleManagerDialog.h" #include "ui_TitleImportDialog.h" -using namespace Platform; +using namespace melonDS; +using namespace melonDS::Platform; -bool TitleManagerDialog::NANDInited = false; +std::unique_ptr TitleManagerDialog::nand = nullptr; TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr; extern std::string EmuDirectory; -TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(new Ui::TitleManagerDialog) +TitleManagerDialog::TitleManagerDialog(QWidget* parent, DSi_NAND::NANDImage& image) : QDialog(parent), ui(new Ui::TitleManagerDialog), nandmount(image) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -47,7 +48,7 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne const u32 category = 0x00030004; std::vector titlelist; - DSi_NAND::ListTitles(category, titlelist); + nandmount.ListTitles(category, titlelist); for (std::vector::iterator it = titlelist.begin(); it != titlelist.end(); it++) { @@ -109,11 +110,11 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) NDSHeader header; NDSBanner banner; - DSi_NAND::GetTitleInfo(category, titleid, version, &header, &banner); + nandmount.GetTitleInfo(category, titleid, version, &header, &banner); u32 icondata[32*32]; ROMManager::ROMIcon(banner.Icon, banner.Palette, icondata); - QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_ARGB32); + QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_RGBA8888); QIcon icon(QPixmap::fromImage(iconimg.copy())); // TODO: make it possible to select other languages? @@ -137,7 +138,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) bool TitleManagerDialog::openNAND() { - NANDInited = false; + nand = nullptr; FileHandle* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, FileMode::Read); if (!bios7i) @@ -148,22 +149,25 @@ bool TitleManagerDialog::openNAND() FileRead(es_keyY, 16, 1, bios7i); CloseFile(bios7i); - if (!DSi_NAND::Init(es_keyY)) - { + FileHandle* nandfile = Platform::OpenLocalFile(Config::DSiNANDPath, FileMode::ReadWriteExisting); + if (!nandfile) return false; + + nand = std::make_unique(nandfile, es_keyY); + if (!*nand) + { // If loading and mounting the NAND image failed... + nand = nullptr; + return false; + // NOTE: The NANDImage takes ownership of the FileHandle, + // so it will be closed even if the NANDImage constructor fails. } - NANDInited = true; return true; } void TitleManagerDialog::closeNAND() { - if (NANDInited) - { - DSi_NAND::DeInit(); - NANDInited = false; - } + nand = nullptr; } void TitleManagerDialog::done(int r) @@ -175,7 +179,7 @@ void TitleManagerDialog::done(int r) void TitleManagerDialog::on_btnImportTitle_clicked() { - TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, &importTmdData, importReadOnly); + TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, &importTmdData, importReadOnly, nandmount); importdlg->open(); connect(importdlg, &TitleImportDialog::finished, this, &TitleManagerDialog::onImportTitleFinished); @@ -190,14 +194,16 @@ void TitleManagerDialog::onImportTitleFinished(int res) titleid[0] = importTmdData.GetCategory(); titleid[1] = importTmdData.GetID(); + assert(nand != nullptr); + assert(*nand); // remove anything that might hinder the install - DSi_NAND::DeleteTitle(titleid[0], titleid[1]); + nandmount.DeleteTitle(titleid[0], titleid[1]); - bool importres = DSi_NAND::ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly); + bool importres = nandmount.ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly); if (!importres) { // remove a potential half-completed install - DSi_NAND::DeleteTitle(titleid[0], titleid[1]); + nandmount.DeleteTitle(titleid[0], titleid[1]); QMessageBox::critical(this, "Import title - melonDS", @@ -224,7 +230,7 @@ void TitleManagerDialog::on_btnDeleteTitle_clicked() return; u64 titleid = cur->data(Qt::UserRole).toULongLong(); - DSi_NAND::DeleteTitle((u32)(titleid >> 32), (u32)titleid); + nandmount.DeleteTitle((u32)(titleid >> 32), (u32)titleid); delete cur; } @@ -317,7 +323,7 @@ void TitleManagerDialog::onImportTitleData() } u64 titleid = cur->data(Qt::UserRole).toULongLong(); - bool res = DSi_NAND::ImportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); + bool res = nandmount.ImportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); if (!res) { QMessageBox::critical(this, @@ -370,7 +376,7 @@ void TitleManagerDialog::onExportTitleData() if (file.isEmpty()) return; u64 titleid = cur->data(Qt::UserRole).toULongLong(); - bool res = DSi_NAND::ExportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); + bool res = nandmount.ExportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); if (!res) { QMessageBox::critical(this, @@ -380,8 +386,8 @@ void TitleManagerDialog::onExportTitleData() } -TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly) -: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly) +TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly, DSi_NAND::NANDMount& nandmount) +: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly), nandmount(nandmount) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -455,7 +461,7 @@ void TitleImportDialog::accept() } } - if (DSi_NAND::TitleExists(titleid[1], titleid[0])) + if (nandmount.TitleExists(titleid[1], titleid[0])) { if (QMessageBox::question(this, "Import title - melonDS", diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index fc92fd81..2e392ebf 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,6 +19,7 @@ #ifndef TITLEMANAGERDIALOG_H #define TITLEMANAGERDIALOG_H +#include #include #include #include @@ -30,6 +31,7 @@ #include #include "DSi_TMD.h" +#include "DSi_NAND.h" namespace Ui { @@ -44,10 +46,10 @@ class TitleManagerDialog : public QDialog Q_OBJECT public: - explicit TitleManagerDialog(QWidget* parent); + explicit TitleManagerDialog(QWidget* parent, melonDS::DSi_NAND::NANDImage& image); ~TitleManagerDialog(); - static bool NANDInited; + static std::unique_ptr nand; static bool openNAND(); static void closeNAND(); @@ -68,7 +70,10 @@ public: return nullptr; } - currentDlg = new TitleManagerDialog(parent); + assert(nand != nullptr); + assert(*nand); + + currentDlg = new TitleManagerDialog(parent, *nand); currentDlg->open(); return currentDlg; } @@ -89,16 +94,17 @@ private slots: void onExportTitleData(); private: + melonDS::DSi_NAND::NANDMount nandmount; Ui::TitleManagerDialog* ui; QString importAppPath; - DSi_TMD::TitleMetadata importTmdData; + melonDS::DSi_TMD::TitleMetadata importTmdData; bool importReadOnly; QAction* actImportTitleData[3]; QAction* actExportTitleData[3]; - void createTitleItem(u32 category, u32 titleid); + void createTitleItem(melonDS::u32 category, melonDS::u32 titleid); }; class TitleImportDialog : public QDialog @@ -106,7 +112,7 @@ class TitleImportDialog : public QDialog Q_OBJECT public: - explicit TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly); + explicit TitleImportDialog(QWidget* parent, QString& apppath, const melonDS::DSi_TMD::TitleMetadata* tmd, bool& readonly, melonDS::DSi_NAND::NANDMount& nand); ~TitleImportDialog(); private slots: @@ -119,6 +125,7 @@ private slots: private: Ui::TitleImportDialog* ui; + melonDS::DSi_NAND::NANDMount& nandmount; QButtonGroup* grpTmdSource; @@ -126,10 +133,10 @@ private: QNetworkReply* netreply; QString& appPath; - const DSi_TMD::TitleMetadata* tmdData; + const melonDS::DSi_TMD::TitleMetadata* tmdData; bool& readOnly; - u32 titleid[2]; + melonDS::u32 titleid[2]; }; #endif // TITLEMANAGERDIALOG_H diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 5ef10c5a..52076ae2 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h index 166a8266..97e0dbd0 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.h +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index 9bf265e9..d71657a3 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.h b/src/frontend/qt_sdl/WifiSettingsDialog.h index da949243..82e1bd49 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.h +++ b/src/frontend/qt_sdl/WifiSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp new file mode 100644 index 00000000..a99546bd --- /dev/null +++ b/src/frontend/qt_sdl/Window.cpp @@ -0,0 +1,2058 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#ifndef APPLE +#include +#endif +#endif + +#include "main.h" +#include "Input.h" +#include "CheatsDialog.h" +#include "DateTimeDialog.h" +#include "EmuSettingsDialog.h" +#include "InputConfig/InputConfigDialog.h" +#include "VideoSettingsDialog.h" +#include "CameraSettingsDialog.h" +#include "AudioSettingsDialog.h" +#include "FirmwareSettingsDialog.h" +#include "PathSettingsDialog.h" +#include "MPSettingsDialog.h" +#include "WifiSettingsDialog.h" +#include "InterfaceSettingsDialog.h" +#include "ROMInfoDialog.h" +#include "RAMInfoDialog.h" +#include "TitleManagerDialog.h" +#include "PowerManagement/PowerManagementDialog.h" +#include "AudioInOut.h" + +#include "Platform.h" +#include "Config.h" +#include "version.h" +#include "Savestate.h" +#include "LocalMP.h" + +//#include "main_shaders.h" + +#include "ROMManager.h" +#include "ArchiveUtil.h" +#include "CameraManager.h" + +using namespace melonDS; + +// TEMP +extern MainWindow* mainWindow; +extern EmuThread* emuThread; +extern bool RunningSomething; +extern QString NdsRomMimeType; +extern QStringList NdsRomExtensions; +extern QString GbaRomMimeType; +extern QStringList GbaRomExtensions; +extern QStringList ArchiveMimeTypes; +extern QStringList ArchiveExtensions; +/*static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs); +static bool MimeTypeInList(const QMimeType& mimetype, const QStringList& superTypeNames); +static bool NdsRomByExtension(const QString& filename); +static bool GbaRomByExtension(const QString& filename); +static bool SupportedArchiveByExtension(const QString& filename); +static bool NdsRomByMimetype(const QMimeType& mimetype); +static bool GbaRomByMimetype(const QMimeType& mimetype); +static bool SupportedArchiveByMimetype(const QMimeType& mimetype); +static bool ZstdNdsRomByExtension(const QString& filename); +static bool ZstdGbaRomByExtension(const QString& filename); +static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive);*/ + +extern CameraManager* camManager[2]; +extern bool camStarted[2]; + +extern int videoRenderer; +extern bool videoSettingsDirty; + + +// AAAAAAA +static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs = Qt::CaseInsensitive) +{ + return std::any_of(extensions.cbegin(), extensions.cend(), [&](const auto& ext) { + return filename.endsWith(ext, cs); + }); +} + +static bool MimeTypeInList(const QMimeType& mimetype, const QStringList& superTypeNames) +{ + return std::any_of(superTypeNames.cbegin(), superTypeNames.cend(), [&](const auto& superTypeName) { + return mimetype.inherits(superTypeName); + }); +} + + +static bool NdsRomByExtension(const QString& filename) +{ + return FileExtensionInList(filename, NdsRomExtensions); +} + +static bool GbaRomByExtension(const QString& filename) +{ + return FileExtensionInList(filename, GbaRomExtensions); +} + +static bool SupportedArchiveByExtension(const QString& filename) +{ + return FileExtensionInList(filename, ArchiveExtensions); +} + + +static bool NdsRomByMimetype(const QMimeType& mimetype) +{ + return mimetype.inherits(NdsRomMimeType); +} + +static bool GbaRomByMimetype(const QMimeType& mimetype) +{ + return mimetype.inherits(GbaRomMimeType); +} + +static bool SupportedArchiveByMimetype(const QMimeType& mimetype) +{ + return MimeTypeInList(mimetype, ArchiveMimeTypes); +} + +static bool ZstdNdsRomByExtension(const QString& filename) +{ + return filename.endsWith(".zst", Qt::CaseInsensitive) && + NdsRomByExtension(filename.left(filename.size() - 4)); +} + +static bool ZstdGbaRomByExtension(const QString& filename) +{ + return filename.endsWith(".zst", Qt::CaseInsensitive) && + GbaRomByExtension(filename.left(filename.size() - 4)); +} + +static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive = false) +{ + if (ZstdNdsRomByExtension(filename) || ZstdGbaRomByExtension(filename)) + return true; + + if (NdsRomByExtension(filename) || GbaRomByExtension(filename) || SupportedArchiveByExtension(filename)) + return true; + + const auto matchmode = insideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; + const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchmode); + return NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype) || SupportedArchiveByMimetype(mimetype); +} + + +#ifndef _WIN32 +static int signalFd[2]; +QSocketNotifier *signalSn; + +static void signalHandler(int) +{ + char a = 1; + write(signalFd[0], &a, sizeof(a)); +} +#endif + +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) +{ +#ifndef _WIN32 + if (socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) + { + qFatal("Couldn't create socketpair"); + } + + signalSn = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); + connect(signalSn, SIGNAL(activated(int)), this, SLOT(onQuit())); + + struct sigaction sa; + + sa.sa_handler = signalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_flags |= SA_RESTART; + sigaction(SIGINT, &sa, 0); +#endif + + oldW = Config::WindowWidth; + oldH = Config::WindowHeight; + oldMax = Config::WindowMaximized; + + setWindowTitle("melonDS " MELONDS_VERSION); + setAttribute(Qt::WA_DeleteOnClose); + setAcceptDrops(true); + setFocusPolicy(Qt::ClickFocus); + + int inst = Platform::InstanceID(); + + QMenuBar* menubar = new QMenuBar(); + { + QMenu* menu = menubar->addMenu("File"); + + actOpenROM = menu->addAction("Open ROM..."); + connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); + actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); + + /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); + connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); + actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ + + recentMenu = menu->addMenu("Open recent"); + for (int i = 0; i < 10; ++i) + { + std::string item = Config::RecentROMList[i]; + if (!item.empty()) + recentFileList.push_back(QString::fromStdString(item)); + } + updateRecentFilesMenu(); + + //actBootFirmware = menu->addAction("Launch DS menu"); + actBootFirmware = menu->addAction("Boot firmware"); + connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); + + menu->addSeparator(); + + actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel()); + actCurrentCart->setEnabled(false); + + actInsertCart = menu->addAction("Insert cart..."); + connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); + + actEjectCart = menu->addAction("Eject cart"); + connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); + + menu->addSeparator(); + + actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel()); + actCurrentGBACart->setEnabled(false); + + actInsertGBACart = menu->addAction("Insert ROM cart..."); + connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); + + { + QMenu* submenu = menu->addMenu("Insert add-on cart"); + + actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); + actInsertGBAAddon[0]->setData(QVariant(GBAAddon_RAMExpansion)); + connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + } + + actEjectGBACart = menu->addAction("Eject cart"); + connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); + + menu->addSeparator(); + + actImportSavefile = menu->addAction("Import savefile"); + connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); + + menu->addSeparator(); + + { + QMenu* submenu = menu->addMenu("Save state"); + + for (int i = 1; i < 9; i++) + { + actSaveState[i] = submenu->addAction(QString("%1").arg(i)); + actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); + actSaveState[i]->setData(QVariant(i)); + connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); + } + + actSaveState[0] = submenu->addAction("File..."); + actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); + actSaveState[0]->setData(QVariant(0)); + connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); + } + { + QMenu* submenu = menu->addMenu("Load state"); + + for (int i = 1; i < 9; i++) + { + actLoadState[i] = submenu->addAction(QString("%1").arg(i)); + actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); + actLoadState[i]->setData(QVariant(i)); + connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actLoadState[0] = submenu->addAction("File..."); + actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); + actLoadState[0]->setData(QVariant(0)); + connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actUndoStateLoad = menu->addAction("Undo state load"); + actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); + connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); + + menu->addSeparator(); + + actQuit = menu->addAction("Quit"); + connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); + actQuit->setShortcut(QKeySequence(QKeySequence::StandardKey::Quit)); + } + { + QMenu* menu = menubar->addMenu("System"); + + actPause = menu->addAction("Pause"); + actPause->setCheckable(true); + connect(actPause, &QAction::triggered, this, &MainWindow::onPause); + + actReset = menu->addAction("Reset"); + connect(actReset, &QAction::triggered, this, &MainWindow::onReset); + + actStop = menu->addAction("Stop"); + connect(actStop, &QAction::triggered, this, &MainWindow::onStop); + + actFrameStep = menu->addAction("Frame step"); + connect(actFrameStep, &QAction::triggered, this, &MainWindow::onFrameStep); + + menu->addSeparator(); + + actPowerManagement = menu->addAction("Power management"); + connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); + + actDateTime = menu->addAction("Date and time"); + connect(actDateTime, &QAction::triggered, this, &MainWindow::onOpenDateTime); + + menu->addSeparator(); + + actEnableCheats = menu->addAction("Enable cheats"); + actEnableCheats->setCheckable(true); + connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); + + //if (inst == 0) + { + actSetupCheats = menu->addAction("Setup cheat codes"); + actSetupCheats->setMenuRole(QAction::NoRole); + connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); + + menu->addSeparator(); + actROMInfo = menu->addAction("ROM info"); + connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); + + actRAMInfo = menu->addAction("RAM search"); + connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); + + actTitleManager = menu->addAction("Manage DSi titles"); + connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); + } + + { + menu->addSeparator(); + QMenu* submenu = menu->addMenu("Multiplayer"); + + actMPNewInstance = submenu->addAction("Launch new instance"); + connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); + } + } + { + QMenu* menu = menubar->addMenu("Config"); + + actEmuSettings = menu->addAction("Emu settings"); + connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + +#ifdef __APPLE__ + actPreferences = menu->addAction("Preferences..."); + connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + actPreferences->setMenuRole(QAction::PreferencesRole); +#endif + + actInputConfig = menu->addAction("Input and hotkeys"); + connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); + + actVideoSettings = menu->addAction("Video settings"); + connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); + + actCameraSettings = menu->addAction("Camera settings"); + connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); + + actAudioSettings = menu->addAction("Audio settings"); + connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); + + actMPSettings = menu->addAction("Multiplayer settings"); + connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); + + actWifiSettings = menu->addAction("Wifi settings"); + connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); + + actFirmwareSettings = menu->addAction("Firmware settings"); + connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + + actInterfaceSettings = menu->addAction("Interface settings"); + connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); + + actPathSettings = menu->addAction("Path settings"); + connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); + + { + QMenu* submenu = menu->addMenu("Savestate settings"); + + actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); + actSavestateSRAMReloc->setCheckable(true); + connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); + } + + menu->addSeparator(); + + { + QMenu* submenu = menu->addMenu("Screen size"); + + for (int i = 0; i < 4; i++) + { + int data = i+1; + actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); + actScreenSize[i]->setData(QVariant(data)); + connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); + } + } + { + QMenu* submenu = menu->addMenu("Screen rotation"); + grpScreenRotation = new QActionGroup(submenu); + + for (int i = 0; i < Frontend::screenRot_MAX; i++) + { + int data = i*90; + actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); + actScreenRotation[i]->setActionGroup(grpScreenRotation); + actScreenRotation[i]->setData(QVariant(i)); + actScreenRotation[i]->setCheckable(true); + } + + connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); + } + { + QMenu* submenu = menu->addMenu("Screen gap"); + grpScreenGap = new QActionGroup(submenu); + + const int screengap[] = {0, 1, 8, 64, 90, 128}; + + for (int i = 0; i < 6; i++) + { + int data = screengap[i]; + actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); + actScreenGap[i]->setActionGroup(grpScreenGap); + actScreenGap[i]->setData(QVariant(data)); + actScreenGap[i]->setCheckable(true); + } + + connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); + } + { + QMenu* submenu = menu->addMenu("Screen layout"); + grpScreenLayout = new QActionGroup(submenu); + + const char* screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; + + for (int i = 0; i < Frontend::screenLayout_MAX; i++) + { + actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); + actScreenLayout[i]->setActionGroup(grpScreenLayout); + actScreenLayout[i]->setData(QVariant(i)); + actScreenLayout[i]->setCheckable(true); + } + + connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); + + submenu->addSeparator(); + + actScreenSwap = submenu->addAction("Swap screens"); + actScreenSwap->setCheckable(true); + connect(actScreenSwap, &QAction::triggered, this, &MainWindow::onChangeScreenSwap); + } + { + QMenu* submenu = menu->addMenu("Screen sizing"); + grpScreenSizing = new QActionGroup(submenu); + + const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; + + for (int i = 0; i < Frontend::screenSizing_MAX; i++) + { + actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); + actScreenSizing[i]->setActionGroup(grpScreenSizing); + actScreenSizing[i]->setData(QVariant(i)); + actScreenSizing[i]->setCheckable(true); + } + + connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); + + submenu->addSeparator(); + + actIntegerScaling = submenu->addAction("Force integer scaling"); + actIntegerScaling->setCheckable(true); + connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); + } + { + QMenu* submenu = menu->addMenu("Aspect ratio"); + grpScreenAspectTop = new QActionGroup(submenu); + grpScreenAspectBot = new QActionGroup(submenu); + actScreenAspectTop = new QAction*[AspectRatiosNum]; + actScreenAspectBot = new QAction*[AspectRatiosNum]; + + for (int i = 0; i < 2; i++) + { + QActionGroup* group = grpScreenAspectTop; + QAction** actions = actScreenAspectTop; + + if (i == 1) + { + group = grpScreenAspectBot; + submenu->addSeparator(); + actions = actScreenAspectBot; + } + + for (int j = 0; j < AspectRatiosNum; j++) + { + auto ratio = aspectRatios[j]; + QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); + actions[j] = submenu->addAction(label); + actions[j]->setActionGroup(group); + actions[j]->setData(QVariant(ratio.id)); + actions[j]->setCheckable(true); + } + + connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); + } + } + + actScreenFiltering = menu->addAction("Screen filtering"); + actScreenFiltering->setCheckable(true); + connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); + + actShowOSD = menu->addAction("Show OSD"); + actShowOSD->setCheckable(true); + connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); + + menu->addSeparator(); + + actLimitFramerate = menu->addAction("Limit framerate"); + actLimitFramerate->setCheckable(true); + connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); + + actAudioSync = menu->addAction("Audio sync"); + actAudioSync->setCheckable(true); + connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); + } + setMenuBar(menubar); + + resize(Config::WindowWidth, Config::WindowHeight); + + if (Config::FirmwareUsername == "Arisotura") + actMPNewInstance->setText("Fart"); + +#ifdef Q_OS_MAC + QPoint screenCenter = screen()->availableGeometry().center(); + QRect frameGeo = frameGeometry(); + frameGeo.moveCenter(screenCenter); + move(frameGeo.topLeft()); +#endif + + if (oldMax) + showMaximized(); + else + show(); + + createScreenPanel(); + + actEjectCart->setEnabled(false); + actEjectGBACart->setEnabled(false); + + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + } + + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + actImportSavefile->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + actFrameStep->setEnabled(false); + + actDateTime->setEnabled(true); + actPowerManagement->setEnabled(false); + + actSetupCheats->setEnabled(false); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); + + actEnableCheats->setChecked(Config::EnableCheats); + + actROMInfo->setEnabled(false); + actRAMInfo->setEnabled(false); + + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); + + actScreenRotation[Config::ScreenRotation]->setChecked(true); + + for (int i = 0; i < 6; i++) + { + if (actScreenGap[i]->data().toInt() == Config::ScreenGap) + { + actScreenGap[i]->setChecked(true); + break; + } + } + + actScreenLayout[Config::ScreenLayout]->setChecked(true); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + actIntegerScaling->setChecked(Config::IntegerScaling); + + actScreenSwap->setChecked(Config::ScreenSwap); + + for (int i = 0; i < AspectRatiosNum; i++) + { + if (Config::ScreenAspectTop == aspectRatios[i].id) + actScreenAspectTop[i]->setChecked(true); + if (Config::ScreenAspectBot == aspectRatios[i].id) + actScreenAspectBot[i]->setChecked(true); + } + + actScreenFiltering->setChecked(Config::ScreenFilter); + actShowOSD->setChecked(Config::ShowOSD); + + actLimitFramerate->setChecked(Config::LimitFPS); + actAudioSync->setChecked(Config::AudioSync); + + if (inst > 0) + { + actEmuSettings->setEnabled(false); + actVideoSettings->setEnabled(false); + actMPSettings->setEnabled(false); + actWifiSettings->setEnabled(false); + actInterfaceSettings->setEnabled(false); + +#ifdef __APPLE__ + actPreferences->setEnabled(false); +#endif // __APPLE__ + } +} + +MainWindow::~MainWindow() +{ + delete[] actScreenAspectTop; + delete[] actScreenAspectBot; +} + +void MainWindow::osdAddMessage(unsigned int color, const char* fmt, ...) +{ + if (fmt == nullptr) + return; + + char msg[256]; + va_list args; + va_start(args, fmt); + vsnprintf(msg, 256, fmt, args); + va_end(args); + + panel->osdAddMessage(color, msg); +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + if (hasOGL) + { + // we intentionally don't unpause here + emuThread->emuPause(); + emuThread->deinitContext(); + } + + QMainWindow::closeEvent(event); +} + +void MainWindow::createScreenPanel() +{ + hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + if (hasOGL) + { + ScreenPanelGL* panelGL = new ScreenPanelGL(this); + panelGL->show(); + + panel = panelGL; + + panelGL->createContext(); + } + + if (!hasOGL) + { + ScreenPanelNative* panelNative = new ScreenPanelNative(this); + panel = panelNative; + panel->show(); + } + setCentralWidget(panel); + + actScreenFiltering->setEnabled(hasOGL); + panel->osdSetEnabled(Config::ShowOSD); + + connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); + emit screenLayoutChange(); +} + +GL::Context* MainWindow::getOGLContext() +{ + if (!hasOGL) return nullptr; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->getContext(); +} + +/*void MainWindow::initOpenGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->initOpenGL(); +} + +void MainWindow::deinitOpenGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->deinitOpenGL(); +} + +void MainWindow::drawScreenGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->drawScreenGL(); +}*/ + +void MainWindow::resizeEvent(QResizeEvent* event) +{ + int w = event->size().width(); + int h = event->size().height(); + + if (!isFullScreen()) + { + // this is ugly + // thing is, when maximizing the window, we first receive the resizeEvent + // with a new size matching the screen, then the changeEvent telling us that + // the maximized flag was updated + oldW = Config::WindowWidth; + oldH = Config::WindowHeight; + oldMax = isMaximized(); + + Config::WindowWidth = w; + Config::WindowHeight = h; + } +} + +void MainWindow::changeEvent(QEvent* event) +{ + if (isMaximized() && !oldMax) + { + Config::WindowWidth = oldW; + Config::WindowHeight = oldH; + } + + Config::WindowMaximized = isMaximized() ? 1:0; +} + +void MainWindow::keyPressEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) return; + + // TODO!! REMOVE ME IN RELEASE BUILDS!! + //if (event->key() == Qt::Key_F11) emuThread->NDS->debug(0); + + Input::KeyPress(event); +} + +void MainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) return; + + Input::KeyRelease(event); +} + + +void MainWindow::dragEnterEvent(QDragEnterEvent* event) +{ + if (!event->mimeData()->hasUrls()) return; + + QList urls = event->mimeData()->urls(); + if (urls.count() > 1) return; // not handling more than one file at once + + QString filename = urls.at(0).toLocalFile(); + + if (FileIsSupportedFiletype(filename)) + event->acceptProposedAction(); +} + +void MainWindow::dropEvent(QDropEvent* event) +{ + if (!event->mimeData()->hasUrls()) return; + + QList urls = event->mimeData()->urls(); + if (urls.count() > 1) return; // not handling more than one file at once + + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + const QStringList file = splitArchivePath(urls.at(0).toLocalFile(), false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + const QString filename = file.last(); + const bool romInsideArchive = file.size() > 1; + const auto matchMode = romInsideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; + const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchMode); + + bool isNdsRom = NdsRomByExtension(filename) || NdsRomByMimetype(mimetype); + bool isGbaRom = GbaRomByExtension(filename) || GbaRomByMimetype(mimetype); + isNdsRom |= ZstdNdsRomByExtension(filename); + isGbaRom |= ZstdGbaRomByExtension(filename); + + if (isNdsRom) + { + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) + { + emuThread->emuUnpause(); + return; + } + + const QString barredFilename = file.join('|'); + recentFileList.removeAll(barredFilename); + recentFileList.prepend(barredFilename); + updateRecentFilesMenu(); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); + + updateCartInserted(false); + } + else if (isGbaRom) + { + if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, file)) + { + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(true); + } + else + { + QMessageBox::critical(this, "melonDS", "The file could not be recognized as a DS or GBA ROM."); + emuThread->emuUnpause(); + return; + } +} + +void MainWindow::focusInEvent(QFocusEvent* event) +{ + AudioInOut::AudioMute(mainWindow); +} + +void MainWindow::focusOutEvent(QFocusEvent* event) +{ + AudioInOut::AudioMute(mainWindow); +} + +void MainWindow::onAppStateChanged(Qt::ApplicationState state) +{ + if (state == Qt::ApplicationInactive) + { + Input::KeyReleaseAll(); + if (Config::PauseLostFocus && emuThread->emuIsRunning()) + emuThread->emuPause(); + } + else if (state == Qt::ApplicationActive) + { + if (Config::PauseLostFocus && !pausedManually) + emuThread->emuUnpause(); + } +} + +bool MainWindow::verifySetup() +{ + QString res = ROMManager::VerifySetup(); + if (!res.isEmpty()) + { + QMessageBox::critical(this, "melonDS", res); + return false; + } + + return true; +} + +bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) +{ + if (!verifySetup()) + { + return false; + } + + bool gbaloaded = false; + if (!gbafile.isEmpty()) + { + if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, gbafile)) return false; + + gbaloaded = true; + } + + bool ndsloaded = false; + if (!file.isEmpty()) + { + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) return false; + + recentFileList.removeAll(file.join("|")); + recentFileList.prepend(file.join("|")); + updateRecentFilesMenu(); + ndsloaded = true; + } + + if (boot) + { + if (ndsloaded) + { + emuThread->NDS->Start(); + emuThread->emuRun(); + } + else + { + onBootFirmware(); + } + } + + updateCartInserted(false); + + if (gbaloaded) + { + updateCartInserted(true); + } + + return true; +} + +QStringList MainWindow::splitArchivePath(const QString& filename, bool useMemberSyntax) +{ + if (filename.isEmpty()) return {}; + +#ifdef ARCHIVE_SUPPORT_ENABLED + if (useMemberSyntax) + { + const QStringList filenameParts = filename.split('|'); + if (filenameParts.size() > 2) + { + QMessageBox::warning(this, "melonDS", "This path contains too many '|'."); + return {}; + } + + if (filenameParts.size() == 2) + { + const QString archive = filenameParts.at(0); + if (!QFileInfo(archive).exists()) + { + QMessageBox::warning(this, "melonDS", "This archive does not exist."); + return {}; + } + + const QString subfile = filenameParts.at(1); + if (!Archive::ListArchive(archive).contains(subfile)) + { + QMessageBox::warning(this, "melonDS", "This archive does not contain the desired file."); + return {}; + } + + return filenameParts; + } + } +#endif + + if (!QFileInfo(filename).exists()) + { + QMessageBox::warning(this, "melonDS", "This ROM file does not exist."); + return {}; + } + +#ifdef ARCHIVE_SUPPORT_ENABLED + if (SupportedArchiveByExtension(filename) + || SupportedArchiveByMimetype(QMimeDatabase().mimeTypeForFile(filename))) + { + const QString subfile = pickFileFromArchive(filename); + if (subfile.isEmpty()) + return {}; + + return { filename, subfile }; + } +#endif + + return { filename }; +} + +QString MainWindow::pickFileFromArchive(QString archiveFileName) +{ + QVector archiveROMList = Archive::ListArchive(archiveFileName); + + if (archiveROMList.size() <= 1) + { + if (!archiveROMList.isEmpty() && archiveROMList.at(0) == "OK") + QMessageBox::warning(this, "melonDS", "This archive is empty."); + else + QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); + return QString(); + } + + archiveROMList.removeFirst(); + + const auto notSupportedRom = [&](const auto& filename){ + if (NdsRomByExtension(filename) || GbaRomByExtension(filename)) + return false; + const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension); + return !(NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype)); + }; + + archiveROMList.erase(std::remove_if(archiveROMList.begin(), archiveROMList.end(), notSupportedRom), + archiveROMList.end()); + + if (archiveROMList.isEmpty()) + { + QMessageBox::warning(this, "melonDS", "This archive does not contain any supported ROMs."); + return QString(); + } + + if (archiveROMList.size() == 1) + return archiveROMList.first(); + + bool ok; + const QString toLoad = QInputDialog::getItem( + this, "melonDS", + "This archive contains multiple files. Select which ROM you want to load.", + archiveROMList.toList(), 0, false, &ok + ); + + if (ok) return toLoad; + + // User clicked on cancel + + return QString(); +} + +QStringList MainWindow::pickROM(bool gba) +{ + const QString console = gba ? "GBA" : "DS"; + const QStringList& romexts = gba ? GbaRomExtensions : NdsRomExtensions; + + QString rawROMs = romexts.join(" *"); + QString extraFilters = ";;" + console + " ROMs (*" + rawROMs; + QString allROMs = rawROMs; + + QString zstdROMs = "*" + romexts.join(".zst *") + ".zst"; + extraFilters += ");;Zstandard-compressed " + console + " ROMs (" + zstdROMs + ")"; + allROMs += " " + zstdROMs; + +#ifdef ARCHIVE_SUPPORT_ENABLED + QString archives = "*" + ArchiveExtensions.join(" *"); + extraFilters += ";;Archives (" + archives + ")"; + allROMs += " " + archives; +#endif + extraFilters += ";;All files (*.*)"; + + const QString filename = QFileDialog::getOpenFileName( + this, "Open " + console + " ROM", + QString::fromStdString(Config::LastROMFolder), + "All supported files (*" + allROMs + ")" + extraFilters + ); + + if (filename.isEmpty()) return {}; + + Config::LastROMFolder = QFileInfo(filename).dir().path().toStdString(); + return splitArchivePath(filename, false); +} + +void MainWindow::updateCartInserted(bool gba) +{ + bool inserted; + if (gba) + { + inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + actEjectGBACart->setEnabled(inserted); + } + else + { + inserted = ROMManager::CartInserted(); + actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); + actEjectCart->setEnabled(inserted); + actImportSavefile->setEnabled(inserted); + actSetupCheats->setEnabled(inserted); + actROMInfo->setEnabled(inserted); + actRAMInfo->setEnabled(inserted); + } +} + +void MainWindow::onOpenFile() +{ + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + QStringList file = pickROM(false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) + { + emuThread->emuUnpause(); + return; + } + + QString filename = file.join('|'); + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); + + updateCartInserted(false); +} + +void MainWindow::onClearRecentFiles() +{ + recentFileList.clear(); + for (int i = 0; i < 10; i++) + Config::RecentROMList[i] = ""; + updateRecentFilesMenu(); +} + +void MainWindow::updateRecentFilesMenu() +{ + recentMenu->clear(); + + for (int i = 0; i < recentFileList.size(); ++i) + { + if (i >= 10) break; + + QString item_full = recentFileList.at(i); + QString item_display = item_full; + int itemlen = item_full.length(); + const int maxlen = 100; + if (itemlen > maxlen) + { + int cut_start = 0; + while (item_full[cut_start] != '/' && item_full[cut_start] != '\\' && + cut_start < itemlen) + cut_start++; + + int cut_end = itemlen-1; + while (((item_full[cut_end] != '/' && item_full[cut_end] != '\\') || + (cut_start+4+(itemlen-cut_end) < maxlen)) && + cut_end > 0) + cut_end--; + + item_display.truncate(cut_start+1); + item_display += "..."; + item_display += QString(item_full).remove(0, cut_end); + } + + QAction *actRecentFile_i = recentMenu->addAction(QString("%1. %2").arg(i+1).arg(item_display)); + actRecentFile_i->setData(item_full); + connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); + + Config::RecentROMList[i] = recentFileList.at(i).toStdString(); + } + + while (recentFileList.size() > 10) + recentFileList.removeLast(); + + recentMenu->addSeparator(); + + QAction *actClearRecentList = recentMenu->addAction("Clear"); + connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles); + + if (recentFileList.empty()) + actClearRecentList->setEnabled(false); + + Config::Save(); +} + +void MainWindow::onClickRecentFile() +{ + QAction *act = (QAction *)sender(); + QString filename = act->data().toString(); + + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + const QStringList file = splitArchivePath(filename, true); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) + { + emuThread->emuUnpause(); + return; + } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); + + updateCartInserted(false); +} + +void MainWindow::onBootFirmware() +{ + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::BootToMenu(emuThread)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); + emuThread->emuUnpause(); + return; + } + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); +} + +void MainWindow::onInsertCart() +{ + emuThread->emuPause(); + + QStringList file = pickROM(false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(mainWindow, emuThread, file, false)) + { + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onEjectCart() +{ + emuThread->emuPause(); + + ROMManager::EjectCart(*emuThread->NDS); + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onInsertGBACart() +{ + emuThread->emuPause(); + + QStringList file = pickROM(true); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, file)) + { + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onInsertGBAAddon() +{ + QAction* act = (QAction*)sender(); + int type = act->data().toInt(); + + emuThread->emuPause(); + + ROMManager::LoadGBAAddon(*emuThread->NDS, type); + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onEjectGBACart() +{ + emuThread->emuPause(); + + ROMManager::EjectGBACart(*emuThread->NDS); + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onSaveState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(); + + std::string filename; + if (slot > 0) + { + filename = ROMManager::GetSavestateName(slot); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getSaveFileName(this, + "Save state", + QString::fromStdString(Config::LastROMFolder), + "melonDS savestates (*.mln);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + filename = qfilename.toStdString(); + } + + if (ROMManager::SaveState(*emuThread->NDS, filename)) + { + if (slot > 0) osdAddMessage(0, "State saved to slot %d", slot); + else osdAddMessage(0, "State saved to file"); + + actLoadState[slot]->setEnabled(true); + } + else + { + osdAddMessage(0xFFA0A0, "State save failed"); + } + + emuThread->emuUnpause(); +} + +void MainWindow::onLoadState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(); + + std::string filename; + if (slot > 0) + { + filename = ROMManager::GetSavestateName(slot); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getOpenFileName(this, + "Load state", + QString::fromStdString(Config::LastROMFolder), + "melonDS savestates (*.ml*);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + filename = qfilename.toStdString(); + } + + if (!Platform::FileExists(filename)) + { + if (slot > 0) osdAddMessage(0xFFA0A0, "State slot %d is empty", slot); + else osdAddMessage(0xFFA0A0, "State file does not exist"); + + emuThread->emuUnpause(); + return; + } + + if (ROMManager::LoadState(*emuThread->NDS, filename)) + { + if (slot > 0) osdAddMessage(0, "State loaded from slot %d", slot); + else osdAddMessage(0, "State loaded from file"); + + actUndoStateLoad->setEnabled(true); + } + else + { + osdAddMessage(0xFFA0A0, "State load failed"); + } + + emuThread->emuUnpause(); +} + +void MainWindow::onUndoStateLoad() +{ + emuThread->emuPause(); + ROMManager::UndoStateLoad(*emuThread->NDS); + emuThread->emuUnpause(); + + osdAddMessage(0, "State load undone"); +} + +void MainWindow::onImportSavefile() +{ + emuThread->emuPause(); + QString path = QFileDialog::getOpenFileName(this, + "Select savefile", + QString::fromStdString(Config::LastROMFolder), + "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); + + if (path.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + Platform::FileHandle* f = Platform::OpenFile(path.toStdString(), Platform::FileMode::Read); + if (!f) + { + QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); + emuThread->emuUnpause(); + return; + } + + if (RunningSomething) + { + if (QMessageBox::warning(this, + "melonDS", + "The emulation will be reset and the current savefile overwritten.", + QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) + { + emuThread->emuUnpause(); + return; + } + + ROMManager::Reset(emuThread); + } + + u32 len = FileLength(f); + + std::unique_ptr data = std::make_unique(len); + Platform::FileRewind(f); + Platform::FileRead(data.get(), len, 1, f); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->SetNDSSave(data.get(), len); + + CloseFile(f); + emuThread->emuUnpause(); +} + +void MainWindow::onQuit() +{ +#ifndef _WIN32 + signalSn->setEnabled(false); +#endif + QApplication::quit(); +} + + +void MainWindow::onPause(bool checked) +{ + if (!RunningSomething) return; + + if (checked) + { + emuThread->emuPause(); + osdAddMessage(0, "Paused"); + pausedManually = true; + } + else + { + emuThread->emuUnpause(); + osdAddMessage(0, "Resumed"); + pausedManually = false; + } +} + +void MainWindow::onReset() +{ + if (!RunningSomething) return; + + emuThread->emuPause(); + + actUndoStateLoad->setEnabled(false); + + ROMManager::Reset(emuThread); + + osdAddMessage(0, "Reset"); + emuThread->emuRun(); +} + +void MainWindow::onStop() +{ + if (!RunningSomething) return; + + emuThread->emuPause(); + emuThread->NDS->Stop(); +} + +void MainWindow::onFrameStep() +{ + if (!RunningSomething) return; + + emuThread->emuFrameStep(); +} + +void MainWindow::onOpenDateTime() +{ + DateTimeDialog* dlg = DateTimeDialog::openDlg(this); +} + +void MainWindow::onOpenPowerManagement() +{ + PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this, emuThread); +} + +void MainWindow::onEnableCheats(bool checked) +{ + Config::EnableCheats = checked?1:0; + ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); +} + +void MainWindow::onSetupCheats() +{ + emuThread->emuPause(); + + CheatsDialog* dlg = CheatsDialog::openDlg(this); + connect(dlg, &CheatsDialog::finished, this, &MainWindow::onCheatsDialogFinished); +} + +void MainWindow::onCheatsDialogFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onROMInfo() +{ + auto cart = emuThread->NDS->NDSCartSlot.GetCart(); + if (cart) + ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this, *cart); +} + +void MainWindow::onRAMInfo() +{ + RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this, emuThread); +} + +void MainWindow::onOpenTitleManager() +{ + TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this); +} + +void MainWindow::onMPNewInstance() +{ + //QProcess::startDetached(QApplication::applicationFilePath()); + QProcess newinst; + newinst.setProgram(QApplication::applicationFilePath()); + newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1)); + +#ifdef __WIN32__ + newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args) + { + args->flags |= CREATE_NEW_CONSOLE; + }); +#endif + + newinst.startDetached(); +} + +void MainWindow::onOpenEmuSettings() +{ + emuThread->emuPause(); + + EmuSettingsDialog* dlg = EmuSettingsDialog::openDlg(this); + connect(dlg, &EmuSettingsDialog::finished, this, &MainWindow::onEmuSettingsDialogFinished); +} + +void MainWindow::onEmuSettingsDialogFinished(int res) +{ + emuThread->emuUnpause(); + + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + actEjectGBACart->setEnabled(false); + } + else + { + actInsertGBACart->setEnabled(true); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(true); + actEjectGBACart->setEnabled(ROMManager::GBACartInserted()); + } + + if (EmuSettingsDialog::needsReset) + onReset(); + + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + + if (!RunningSomething) + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); +} + +void MainWindow::onOpenInputConfig() +{ + emuThread->emuPause(); + + InputConfigDialog* dlg = InputConfigDialog::openDlg(this); + connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); +} + +void MainWindow::onInputConfigFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onOpenVideoSettings() +{ + VideoSettingsDialog* dlg = VideoSettingsDialog::openDlg(this); + connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings); +} + +void MainWindow::onOpenCameraSettings() +{ + emuThread->emuPause(); + + camStarted[0] = camManager[0]->isStarted(); + camStarted[1] = camManager[1]->isStarted(); + if (camStarted[0]) camManager[0]->stop(); + if (camStarted[1]) camManager[1]->stop(); + + CameraSettingsDialog* dlg = CameraSettingsDialog::openDlg(this); + connect(dlg, &CameraSettingsDialog::finished, this, &MainWindow::onCameraSettingsFinished); +} + +void MainWindow::onCameraSettingsFinished(int res) +{ + if (camStarted[0]) camManager[0]->start(); + if (camStarted[1]) camManager[1]->start(); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenAudioSettings() +{ + AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive(), emuThread); + connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel); + connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset); + connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); + connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); +} + +void MainWindow::onOpenFirmwareSettings() +{ + emuThread->emuPause(); + + FirmwareSettingsDialog* dlg = FirmwareSettingsDialog::openDlg(this); + connect(dlg, &FirmwareSettingsDialog::finished, this, &MainWindow::onFirmwareSettingsFinished); +} + +void MainWindow::onFirmwareSettingsFinished(int res) +{ + if (FirmwareSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenPathSettings() +{ + emuThread->emuPause(); + + PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this); + connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished); +} + +void MainWindow::onPathSettingsFinished(int res) +{ + if (PathSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + +void MainWindow::onUpdateAudioSettings() +{ + assert(emuThread->NDS != nullptr); + emuThread->NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); + + if (Config::AudioBitDepth == 0) + emuThread->NDS->SPU.SetDegrade10Bit(emuThread->NDS->ConsoleType == 0); + else + emuThread->NDS->SPU.SetDegrade10Bit(Config::AudioBitDepth == 1); +} + +void MainWindow::onAudioSettingsFinished(int res) +{ + AudioInOut::UpdateSettings(*emuThread->NDS); +} + +void MainWindow::onOpenMPSettings() +{ + emuThread->emuPause(); + + MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this); + connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished); +} + +void MainWindow::onMPSettingsFinished(int res) +{ + AudioInOut::AudioMute(mainWindow); + LocalMP::SetRecvTimeout(Config::MPRecvTimeout); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenWifiSettings() +{ + emuThread->emuPause(); + + WifiSettingsDialog* dlg = WifiSettingsDialog::openDlg(this); + connect(dlg, &WifiSettingsDialog::finished, this, &MainWindow::onWifiSettingsFinished); +} + +void MainWindow::onWifiSettingsFinished(int res) +{ + Platform::LAN_DeInit(); + Platform::LAN_Init(); + + if (WifiSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenInterfaceSettings() +{ + emuThread->emuPause(); + InterfaceSettingsDialog* dlg = InterfaceSettingsDialog::openDlg(this); + connect(dlg, &InterfaceSettingsDialog::finished, this, &MainWindow::onInterfaceSettingsFinished); + connect(dlg, &InterfaceSettingsDialog::updateMouseTimer, this, &MainWindow::onUpdateMouseTimer); +} + +void MainWindow::onUpdateMouseTimer() +{ + panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000); +} + +void MainWindow::onInterfaceSettingsFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onChangeSavestateSRAMReloc(bool checked) +{ + Config::SavestateRelocSRAM = checked?1:0; +} + +void MainWindow::onChangeScreenSize() +{ + int factor = ((QAction*)sender())->data().toInt(); + QSize diff = size() - panel->size(); + resize(panel->screenGetMinSize(factor) + diff); +} + +void MainWindow::onChangeScreenRotation(QAction* act) +{ + int rot = act->data().toInt(); + Config::ScreenRotation = rot; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenGap(QAction* act) +{ + int gap = act->data().toInt(); + Config::ScreenGap = gap; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenLayout(QAction* act) +{ + int layout = act->data().toInt(); + Config::ScreenLayout = layout; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenSwap(bool checked) +{ + Config::ScreenSwap = checked?1:0; + + // Swap between top and bottom screen when displaying one screen. + if (Config::ScreenSizing == Frontend::screenSizing_TopOnly) + { + // Bottom Screen. + Config::ScreenSizing = Frontend::screenSizing_BotOnly; + actScreenSizing[Frontend::screenSizing_TopOnly]->setChecked(false); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + } + else if (Config::ScreenSizing == Frontend::screenSizing_BotOnly) + { + // Top Screen. + Config::ScreenSizing = Frontend::screenSizing_TopOnly; + actScreenSizing[Frontend::screenSizing_BotOnly]->setChecked(false); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + } + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenSizing(QAction* act) +{ + int sizing = act->data().toInt(); + Config::ScreenSizing = sizing; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenAspect(QAction* act) +{ + int aspect = act->data().toInt(); + QActionGroup* group = act->actionGroup(); + + if (group == grpScreenAspectTop) + { + Config::ScreenAspectTop = aspect; + } + else + { + Config::ScreenAspectBot = aspect; + } + + emit screenLayoutChange(); +} + +void MainWindow::onChangeIntegerScaling(bool checked) +{ + Config::IntegerScaling = checked?1:0; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenFiltering(bool checked) +{ + Config::ScreenFilter = checked?1:0; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeShowOSD(bool checked) +{ + Config::ShowOSD = checked?1:0; + panel->osdSetEnabled(Config::ShowOSD); +} + +void MainWindow::onChangeLimitFramerate(bool checked) +{ + Config::LimitFPS = checked?1:0; +} + +void MainWindow::onChangeAudioSync(bool checked) +{ + Config::AudioSync = checked?1:0; +} + + +void MainWindow::onTitleUpdate(QString title) +{ + setWindowTitle(title); +} + +void ToggleFullscreen(MainWindow* mainWindow) +{ + if (!mainWindow->isFullScreen()) + { + mainWindow->showFullScreen(); + mainWindow->menuBar()->setFixedHeight(0); // Don't use hide() as menubar actions stop working + } + else + { + mainWindow->showNormal(); + int menuBarHeight = mainWindow->menuBar()->sizeHint().height(); + mainWindow->menuBar()->setFixedHeight(menuBarHeight); + } +} + +void MainWindow::onFullscreenToggled() +{ + ToggleFullscreen(this); +} + +void MainWindow::onScreenEmphasisToggled() +{ + int currentSizing = Config::ScreenSizing; + if (currentSizing == Frontend::screenSizing_EmphTop) + { + Config::ScreenSizing = Frontend::screenSizing_EmphBot; + } + else if (currentSizing == Frontend::screenSizing_EmphBot) + { + Config::ScreenSizing = Frontend::screenSizing_EmphTop; + } + + emit screenLayoutChange(); +} + +void MainWindow::onEmuStart() +{ + for (int i = 1; i < 9; i++) + { + actSaveState[i]->setEnabled(true); + actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); + } + actSaveState[0]->setEnabled(true); + actLoadState[0]->setEnabled(true); + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(true); + actPause->setChecked(false); + actReset->setEnabled(true); + actStop->setEnabled(true); + actFrameStep->setEnabled(true); + + actDateTime->setEnabled(false); + actPowerManagement->setEnabled(true); + + actTitleManager->setEnabled(false); +} + +void MainWindow::onEmuStop() +{ + emuThread->emuPause(); + + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + actFrameStep->setEnabled(false); + + actDateTime->setEnabled(true); + actPowerManagement->setEnabled(false); + + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); +} + +void MainWindow::onUpdateVideoSettings(bool glchange) +{ + if (glchange) + { + emuThread->emuPause(); + if (hasOGL) emuThread->deinitContext(); + + delete panel; + createScreenPanel(); + connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint())); + } + + videoSettingsDirty = true; + + if (glchange) + { + if (hasOGL) emuThread->initContext(); + emuThread->emuUnpause(); + } +} diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h new file mode 100644 index 00000000..bc207480 --- /dev/null +++ b/src/frontend/qt_sdl/Window.h @@ -0,0 +1,299 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef WINDOW_H +#define WINDOW_H + +#include "glad/glad.h" +#include "FrontendUtil.h" +#include "duckstation/gl/context.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Screen.h" + + +class EmuThread; + +/* +class WindowBase : public QMainWindow +{ + Q_OBJECT + +public: + explicit WindowBase(QWidget* parent = nullptr); + ~WindowBase(); + + bool hasOGL; + GL::Context* getOGLContext(); + + //void onAppStateChanged(Qt::ApplicationState state); + +protected: + void resizeEvent(QResizeEvent* event) override; + void changeEvent(QEvent* event) override; + + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + +signals: + void screenLayoutChange(); + +private slots: + //void onQuit(); + + //void onTitleUpdate(QString title); + + //void onEmuStart(); + //void onEmuStop(); + + //void onUpdateVideoSettings(bool glchange); + + void onFullscreenToggled(); + void onScreenEmphasisToggled(); + +private: + virtual void closeEvent(QCloseEvent* event) override; + + void createScreenPanel(); + + //bool pausedManually = false; + + int oldW, oldH; + bool oldMax; + +public: + ScreenPanel* panel; +};*/ + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + + bool hasOGL; + GL::Context* getOGLContext(); + /*void initOpenGL(); + void deinitOpenGL(); + void drawScreenGL();*/ + + bool preloadROMs(QStringList file, QStringList gbafile, bool boot); + QStringList splitArchivePath(const QString& filename, bool useMemberSyntax); + + void onAppStateChanged(Qt::ApplicationState state); + + void osdAddMessage(unsigned int color, const char* fmt, ...); + +protected: + void resizeEvent(QResizeEvent* event) override; + void changeEvent(QEvent* event) override; + + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + +signals: + void screenLayoutChange(); + +private slots: + void onOpenFile(); + void onClickRecentFile(); + void onClearRecentFiles(); + void onBootFirmware(); + void onInsertCart(); + void onEjectCart(); + void onInsertGBACart(); + void onInsertGBAAddon(); + void onEjectGBACart(); + void onSaveState(); + void onLoadState(); + void onUndoStateLoad(); + void onImportSavefile(); + void onQuit(); + + void onPause(bool checked); + void onReset(); + void onStop(); + void onFrameStep(); + void onOpenPowerManagement(); + void onOpenDateTime(); + void onEnableCheats(bool checked); + void onSetupCheats(); + void onCheatsDialogFinished(int res); + void onROMInfo(); + void onRAMInfo(); + void onOpenTitleManager(); + void onMPNewInstance(); + + void onOpenEmuSettings(); + void onEmuSettingsDialogFinished(int res); + void onOpenInputConfig(); + void onInputConfigFinished(int res); + void onOpenVideoSettings(); + void onOpenCameraSettings(); + void onCameraSettingsFinished(int res); + void onOpenAudioSettings(); + void onUpdateAudioSettings(); + void onAudioSettingsFinished(int res); + void onOpenMPSettings(); + void onMPSettingsFinished(int res); + void onOpenWifiSettings(); + void onWifiSettingsFinished(int res); + void onOpenFirmwareSettings(); + void onFirmwareSettingsFinished(int res); + void onOpenPathSettings(); + void onPathSettingsFinished(int res); + void onOpenInterfaceSettings(); + void onInterfaceSettingsFinished(int res); + void onUpdateMouseTimer(); + void onChangeSavestateSRAMReloc(bool checked); + void onChangeScreenSize(); + void onChangeScreenRotation(QAction* act); + void onChangeScreenGap(QAction* act); + void onChangeScreenLayout(QAction* act); + void onChangeScreenSwap(bool checked); + void onChangeScreenSizing(QAction* act); + void onChangeScreenAspect(QAction* act); + void onChangeIntegerScaling(bool checked); + void onChangeScreenFiltering(bool checked); + void onChangeShowOSD(bool checked); + void onChangeLimitFramerate(bool checked); + void onChangeAudioSync(bool checked); + + void onTitleUpdate(QString title); + + void onEmuStart(); + void onEmuStop(); + + void onUpdateVideoSettings(bool glchange); + + void onFullscreenToggled(); + void onScreenEmphasisToggled(); + +private: + virtual void closeEvent(QCloseEvent* event) override; + + QStringList currentROM; + QStringList currentGBAROM; + QList recentFileList; + QMenu *recentMenu; + void updateRecentFilesMenu(); + + bool verifySetup(); + QString pickFileFromArchive(QString archiveFileName); + QStringList pickROM(bool gba); + void updateCartInserted(bool gba); + + void createScreenPanel(); + + bool pausedManually = false; + + int oldW, oldH; + bool oldMax; + +public: + ScreenPanel* panel; + + QAction* actOpenROM; + QAction* actBootFirmware; + QAction* actCurrentCart; + QAction* actInsertCart; + QAction* actEjectCart; + QAction* actCurrentGBACart; + QAction* actInsertGBACart; + QAction* actInsertGBAAddon[1]; + QAction* actEjectGBACart; + QAction* actImportSavefile; + QAction* actSaveState[9]; + QAction* actLoadState[9]; + QAction* actUndoStateLoad; + QAction* actQuit; + + QAction* actPause; + QAction* actReset; + QAction* actStop; + QAction* actFrameStep; + QAction* actPowerManagement; + QAction* actDateTime; + QAction* actEnableCheats; + QAction* actSetupCheats; + QAction* actROMInfo; + QAction* actRAMInfo; + QAction* actTitleManager; + QAction* actMPNewInstance; + + QAction* actEmuSettings; +#ifdef __APPLE__ + QAction* actPreferences; +#endif + QAction* actInputConfig; + QAction* actVideoSettings; + QAction* actCameraSettings; + QAction* actAudioSettings; + QAction* actMPSettings; + QAction* actWifiSettings; + QAction* actFirmwareSettings; + QAction* actPathSettings; + QAction* actInterfaceSettings; + QAction* actSavestateSRAMReloc; + QAction* actScreenSize[4]; + QActionGroup* grpScreenRotation; + QAction* actScreenRotation[Frontend::screenRot_MAX]; + QActionGroup* grpScreenGap; + QAction* actScreenGap[6]; + QActionGroup* grpScreenLayout; + QAction* actScreenLayout[Frontend::screenLayout_MAX]; + QAction* actScreenSwap; + QActionGroup* grpScreenSizing; + QAction* actScreenSizing[Frontend::screenSizing_MAX]; + QAction* actIntegerScaling; + QActionGroup* grpScreenAspectTop; + QAction** actScreenAspectTop; + QActionGroup* grpScreenAspectBot; + QAction** actScreenAspectBot; + QAction* actScreenFiltering; + QAction* actShowOSD; + QAction* actLimitFramerate; + QAction* actAudioSync; +}; + +void ToggleFullscreen(MainWindow* mainWindow); + +#endif // WINDOW_H diff --git a/src/frontend/qt_sdl/font.h b/src/frontend/qt_sdl/font.h index 01e3bd29..7945c515 100644 --- a/src/frontend/qt_sdl/font.h +++ b/src/frontend/qt_sdl/font.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/gif-h/gif.h b/src/frontend/qt_sdl/gif-h/gif.h new file mode 100644 index 00000000..d89ffc38 --- /dev/null +++ b/src/frontend/qt_sdl/gif-h/gif.h @@ -0,0 +1,836 @@ +// +// gif.h +// by Charlie Tangora +// Public domain. +// Email me : ctangora -at- gmail -dot- com +// +// This file offers a simple, very limited way to create animated GIFs directly in code. +// +// Those looking for particular cleverness are likely to be disappointed; it's pretty +// much a straight-ahead implementation of the GIF format with optional Floyd-Steinberg +// dithering. (It does at least use delta encoding - only the changed portions of each +// frame are saved.) +// +// So resulting files are often quite large. The hope is that it will be handy nonetheless +// as a quick and easily-integrated way for programs to spit out animations. +// +// Only RGBA8 is currently supported as an input format. (The alpha is ignored.) +// +// If capturing a buffer with a bottom-left origin (such as OpenGL), define GIF_FLIP_VERT +// to automatically flip the buffer data when writing the image (the buffer itself is +// unchanged. +// +// USAGE: +// Create a GifWriter struct. Pass it to GifBegin() to initialize and write the header. +// Pass subsequent frames to GifWriteFrame(). +// Finally, call GifEnd() to close the file handle and free memory. +// + +#ifndef gif_h +#define gif_h + +#include // for FILE* +#include // for memcpy and bzero +#include // for integer typedefs +#include // for bool macros + +// Define these macros to hook into a custom memory allocator. +// TEMP_MALLOC and TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs +// and any temp memory allocated by a function will be freed before it exits. +// MALLOC and FREE are used only by GifBegin and GifEnd respectively (to allocate a buffer the size of the image, which +// is used to find changed pixels for delta-encoding.) + +#ifndef GIF_TEMP_MALLOC +#include +#define GIF_TEMP_MALLOC malloc +#endif + +#ifndef GIF_TEMP_FREE +#include +#define GIF_TEMP_FREE free +#endif + +#ifndef GIF_MALLOC +#include +#define GIF_MALLOC malloc +#endif + +#ifndef GIF_FREE +#include +#define GIF_FREE free +#endif + +const int kGifTransIndex = 0; + +typedef struct +{ + int bitDepth; + + uint8_t r[256]; + uint8_t g[256]; + uint8_t b[256]; + + // k-d tree over RGB space, organized in heap fashion + // i.e. left child of node i is node i*2, right child is node i*2+1 + // nodes 256-511 are implicitly the leaves, containing a color + uint8_t treeSplitElt[256]; + uint8_t treeSplit[256]; +} GifPalette; + +// max, min, and abs functions +int GifIMax(int l, int r) { return l>r?l:r; } +int GifIMin(int l, int r) { return l (1<bitDepth)-1) + { + int ind = treeRoot-(1<bitDepth); + if(ind == kGifTransIndex) return; + + // check whether this color is better than the current winner + int r_err = r - ((int32_t)pPal->r[ind]); + int g_err = g - ((int32_t)pPal->g[ind]); + int b_err = b - ((int32_t)pPal->b[ind]); + int diff = GifIAbs(r_err)+GifIAbs(g_err)+GifIAbs(b_err); + + if(diff < *bestDiff) + { + *bestInd = ind; + *bestDiff = diff; + } + + return; + } + + // take the appropriate color (r, g, or b) for this node of the k-d tree + int comps[3]; comps[0] = r; comps[1] = g; comps[2] = b; + int splitComp = comps[pPal->treeSplitElt[treeRoot]]; + + int splitPos = pPal->treeSplit[treeRoot]; + if(splitPos > splitComp) + { + // check the left subtree + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); + if( *bestDiff > splitPos - splitComp ) + { + // cannot prove there's not a better value in the right subtree, check that too + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); + } + } + else + { + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); + if( *bestDiff > splitComp - splitPos ) + { + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); + } + } +} + +void GifSwapPixels(uint8_t* image, int pixA, int pixB) +{ + uint8_t rA = image[pixA*4]; + uint8_t gA = image[pixA*4+1]; + uint8_t bA = image[pixA*4+2]; + uint8_t aA = image[pixA*4+3]; + + uint8_t rB = image[pixB*4]; + uint8_t gB = image[pixB*4+1]; + uint8_t bB = image[pixB*4+2]; + uint8_t aB = image[pixA*4+3]; + + image[pixA*4] = rB; + image[pixA*4+1] = gB; + image[pixA*4+2] = bB; + image[pixA*4+3] = aB; + + image[pixB*4] = rA; + image[pixB*4+1] = gA; + image[pixB*4+2] = bA; + image[pixB*4+3] = aA; +} + +// just the partition operation from quicksort +int GifPartition(uint8_t* image, const int left, const int right, const int elt, int pivotIndex) +{ + const int pivotValue = image[(pivotIndex)*4+elt]; + GifSwapPixels(image, pivotIndex, right-1); + int storeIndex = left; + bool split = 0; + for(int ii=left; ii neededCenter) + GifPartitionByMedian(image, left, pivotIndex, com, neededCenter); + + if(pivotIndex < neededCenter) + GifPartitionByMedian(image, pivotIndex+1, right, com, neededCenter); + } +} + +// Builds a palette by creating a balanced k-d tree of all pixels in the image +void GifSplitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, bool buildForDither, GifPalette* pal) +{ + if(lastElt <= firstElt || numPixels == 0) + return; + + // base case, bottom of the tree + if(lastElt == firstElt+1) + { + if(buildForDither) + { + // Dithering needs at least one color as dark as anything + // in the image and at least one brightest color - + // otherwise it builds up error and produces strange artifacts + if( firstElt == 1 ) + { + // special case: the darkest color in the image + uint32_t r=255, g=255, b=255; + for(int ii=0; iir[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + + return; + } + + if( firstElt == (1 << pal->bitDepth)-1 ) + { + // special case: the lightest color in the image + uint32_t r=0, g=0, b=0; + for(int ii=0; iir[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + + return; + } + } + + // otherwise, take the average of all colors in this subcube + uint64_t r=0, g=0, b=0; + for(int ii=0; iir[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + + return; + } + + // Find the axis with the largest range + int minR = 255, maxR = 0; + int minG = 255, maxG = 0; + int minB = 255, maxB = 0; + for(int ii=0; ii maxR) maxR = r; + if(r < minR) minR = r; + + if(g > maxG) maxG = g; + if(g < minG) minG = g; + + if(b > maxB) maxB = b; + if(b < minB) minB = b; + } + + int rRange = maxR - minR; + int gRange = maxG - minG; + int bRange = maxB - minB; + + // and split along that axis. (incidentally, this means this isn't a "proper" k-d tree but I don't know what else to call it) + int splitCom = 1; + if(bRange > gRange) splitCom = 2; + if(rRange > bRange && rRange > gRange) splitCom = 0; + + int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); + int subPixelsB = numPixels-subPixelsA; + + GifPartitionByMedian(image, 0, numPixels, splitCom, subPixelsA); + + pal->treeSplitElt[treeNode] = (uint8_t)splitCom; + pal->treeSplit[treeNode] = image[subPixelsA*4+splitCom]; + + GifSplitPalette(image, subPixelsA, firstElt, splitElt, splitElt-splitDist, splitDist/2, treeNode*2, buildForDither, pal); + GifSplitPalette(image+subPixelsA*4, subPixelsB, splitElt, lastElt, splitElt+splitDist, splitDist/2, treeNode*2+1, buildForDither, pal); +} + +// Finds all pixels that have changed from the previous image and +// moves them to the fromt of th buffer. +// This allows us to build a palette optimized for the colors of the +// changed pixels only. +int GifPickChangedPixels( const uint8_t* lastFrame, uint8_t* frame, int numPixels ) +{ + int numChanged = 0; + uint8_t* writeIter = frame; + + for (int ii=0; iibitDepth = bitDepth; + + // SplitPalette is destructive (it sorts the pixels by color) so + // we must create a copy of the image for it to destroy + size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t)); + uint8_t* destroyableImage = (uint8_t*)GIF_TEMP_MALLOC(imageSize); + memcpy(destroyableImage, nextFrame, imageSize); + + int numPixels = (int)(width * height); + if(lastFrame) + numPixels = GifPickChangedPixels(lastFrame, destroyableImage, numPixels); + + const int lastElt = 1 << bitDepth; + const int splitElt = lastElt/2; + const int splitDist = splitElt/2; + + GifSplitPalette(destroyableImage, numPixels, 1, lastElt, splitElt, splitDist, 1, buildForDither, pPal); + + GIF_TEMP_FREE(destroyableImage); + + // add the bottom node for the transparency index + pPal->treeSplit[1 << (bitDepth-1)] = 0; + pPal->treeSplitElt[1 << (bitDepth-1)] = 0; + + pPal->r[0] = pPal->g[0] = pPal->b[0] = 0; +} + +// Implements Floyd-Steinberg dithering, writes palette value to alpha +void GifDitherImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t* outFrame, uint32_t width, uint32_t height, GifPalette* pPal ) +{ + int numPixels = (int)(width * height); + + // quantPixels initially holds color*256 for all pixels + // The extra 8 bits of precision allow for sub-single-color error values + // to be propagated + int32_t *quantPixels = (int32_t *)GIF_TEMP_MALLOC(sizeof(int32_t) * (size_t)numPixels * 4); + + for( int ii=0; iir[bestInd]) * 256; + int32_t g_err = nextPix[1] - (int32_t)(pPal->g[bestInd]) * 256; + int32_t b_err = nextPix[2] - (int32_t)(pPal->b[bestInd]) * 256; + + nextPix[0] = pPal->r[bestInd]; + nextPix[1] = pPal->g[bestInd]; + nextPix[2] = pPal->b[bestInd]; + nextPix[3] = bestInd; + + // Propagate the error to the four adjacent locations + // that we haven't touched yet + int quantloc_7 = (int)(yy * width + xx + 1); + int quantloc_3 = (int)(yy * width + width + xx - 1); + int quantloc_5 = (int)(yy * width + width + xx); + int quantloc_1 = (int)(yy * width + width + xx + 1); + + if(quantloc_7 < numPixels) + { + int32_t* pix7 = quantPixels+4*quantloc_7; + pix7[0] += GifIMax( -pix7[0], r_err * 7 / 16 ); + pix7[1] += GifIMax( -pix7[1], g_err * 7 / 16 ); + pix7[2] += GifIMax( -pix7[2], b_err * 7 / 16 ); + } + + if(quantloc_3 < numPixels) + { + int32_t* pix3 = quantPixels+4*quantloc_3; + pix3[0] += GifIMax( -pix3[0], r_err * 3 / 16 ); + pix3[1] += GifIMax( -pix3[1], g_err * 3 / 16 ); + pix3[2] += GifIMax( -pix3[2], b_err * 3 / 16 ); + } + + if(quantloc_5 < numPixels) + { + int32_t* pix5 = quantPixels+4*quantloc_5; + pix5[0] += GifIMax( -pix5[0], r_err * 5 / 16 ); + pix5[1] += GifIMax( -pix5[1], g_err * 5 / 16 ); + pix5[2] += GifIMax( -pix5[2], b_err * 5 / 16 ); + } + + if(quantloc_1 < numPixels) + { + int32_t* pix1 = quantPixels+4*quantloc_1; + pix1[0] += GifIMax( -pix1[0], r_err / 16 ); + pix1[1] += GifIMax( -pix1[1], g_err / 16 ); + pix1[2] += GifIMax( -pix1[2], b_err / 16 ); + } + } + } + + // Copy the palettized result to the output buffer + for( int ii=0; iir[bestInd]; + outFrame[1] = pPal->g[bestInd]; + outFrame[2] = pPal->b[bestInd]; + outFrame[3] = (uint8_t)bestInd; + } + + if(lastFrame) lastFrame += 4; + outFrame += 4; + nextFrame += 4; + } +} + +// Simple structure to write out the LZW-compressed portion of the image +// one bit at a time +typedef struct +{ + uint8_t bitIndex; // how many bits in the partial byte written so far + uint8_t byte; // current partial byte + + uint32_t chunkIndex; + uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file +} GifBitStatus; + +// insert a single bit +void GifWriteBit( GifBitStatus* stat, uint32_t bit ) +{ + bit = bit & 1; + bit = bit << stat->bitIndex; + stat->byte |= bit; + + ++stat->bitIndex; + if( stat->bitIndex > 7 ) + { + // move the newly-finished byte to the chunk buffer + stat->chunk[stat->chunkIndex++] = stat->byte; + // and start a new byte + stat->bitIndex = 0; + stat->byte = 0; + } +} + +// write all bytes so far to the file +void GifWriteChunk( FILE* f, GifBitStatus* stat ) +{ + fputc((int)stat->chunkIndex, f); + fwrite(stat->chunk, 1, stat->chunkIndex, f); + + stat->bitIndex = 0; + stat->byte = 0; + stat->chunkIndex = 0; +} + +void GifWriteCode( FILE* f, GifBitStatus* stat, uint32_t code, uint32_t length ) +{ + for( uint32_t ii=0; ii> 1; + + if( stat->chunkIndex == 255 ) + { + GifWriteChunk(f, stat); + } + } +} + +// The LZW dictionary is a 256-ary tree constructed as the file is encoded, +// this is one node +typedef struct +{ + uint16_t m_next[256]; +} GifLzwNode; + +// write a 256-color (8-bit) image palette to the file +void GifWritePalette( const GifPalette* pPal, FILE* f ) +{ + fputc(0, f); // first color: transparency + fputc(0, f); + fputc(0, f); + + for(int ii=1; ii<(1 << pPal->bitDepth); ++ii) + { + uint32_t r = pPal->r[ii]; + uint32_t g = pPal->g[ii]; + uint32_t b = pPal->b[ii]; + + fputc((int)r, f); + fputc((int)g, f); + fputc((int)b, f); + } +} + +// write the image header, LZW-compress and write out the image +void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette* pPal) +{ + // graphics control extension + fputc(0x21, f); + fputc(0xf9, f); + fputc(0x04, f); + fputc(0x05, f); // leave prev frame in place, this frame has transparency + fputc(delay & 0xff, f); + fputc((delay >> 8) & 0xff, f); + fputc(kGifTransIndex, f); // transparent color index + fputc(0, f); + + fputc(0x2c, f); // image descriptor block + + fputc(left & 0xff, f); // corner of image in canvas space + fputc((left >> 8) & 0xff, f); + fputc(top & 0xff, f); + fputc((top >> 8) & 0xff, f); + + fputc(width & 0xff, f); // width and height of image + fputc((width >> 8) & 0xff, f); + fputc(height & 0xff, f); + fputc((height >> 8) & 0xff, f); + + //fputc(0, f); // no local color table, no transparency + //fputc(0x80, f); // no local color table, but transparency + + fputc(0x80 + pPal->bitDepth-1, f); // local color table present, 2 ^ bitDepth entries + GifWritePalette(pPal, f); + + const int minCodeSize = pPal->bitDepth; + const uint32_t clearCode = 1 << pPal->bitDepth; + + fputc(minCodeSize, f); // min code size 8 bits + + GifLzwNode* codetree = (GifLzwNode*)GIF_TEMP_MALLOC(sizeof(GifLzwNode)*4096); + + memset(codetree, 0, sizeof(GifLzwNode)*4096); + int32_t curCode = -1; + uint32_t codeSize = (uint32_t)minCodeSize + 1; + uint32_t maxCode = clearCode+1; + + GifBitStatus stat; + stat.byte = 0; + stat.bitIndex = 0; + stat.chunkIndex = 0; + + GifWriteCode(f, &stat, clearCode, codeSize); // start with a fresh LZW dictionary + + for(uint32_t yy=0; yy= (1ul << codeSize) ) + { + // dictionary entry count has broken a size barrier, + // we need more bits for codes + codeSize++; + } + if( maxCode == 4095 ) + { + // the dictionary is full, clear it out and begin anew + GifWriteCode(f, &stat, clearCode, codeSize); // clear tree + + memset(codetree, 0, sizeof(GifLzwNode)*4096); + codeSize = (uint32_t)(minCodeSize + 1); + maxCode = clearCode+1; + } + + curCode = nextValue; + } + } + } + + // compression footer + GifWriteCode(f, &stat, (uint32_t)curCode, codeSize); + GifWriteCode(f, &stat, clearCode, codeSize); + GifWriteCode(f, &stat, clearCode + 1, (uint32_t)minCodeSize + 1); + + // write out the last partial chunk + while( stat.bitIndex ) GifWriteBit(&stat, 0); + if( stat.chunkIndex ) GifWriteChunk(f, &stat); + + fputc(0, f); // image block terminator + + GIF_TEMP_FREE(codetree); +} + +typedef struct +{ + FILE* f; + uint8_t* oldImage; + bool firstFrame; +} GifWriter; + +// Creates a gif file. +// The input GIFWriter is assumed to be uninitialized. +// The delay value is the time between frames in hundredths of a second - note that not all viewers pay much attention to this value. +bool GifBegin( GifWriter* writer, const char* filename, uint32_t width, uint32_t height, uint32_t delay, int32_t bitDepth = 8, bool dither = false ) +{ + (void)bitDepth; (void)dither; // Mute "Unused argument" warnings +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + writer->f = 0; + fopen_s(&writer->f, filename, "wb"); +#else + writer->f = fopen(filename, "wb"); +#endif + if(!writer->f) return false; + + writer->firstFrame = true; + + // allocate + writer->oldImage = (uint8_t*)GIF_MALLOC(width*height*4); + + fputs("GIF89a", writer->f); + + // screen descriptor + fputc(width & 0xff, writer->f); + fputc((width >> 8) & 0xff, writer->f); + fputc(height & 0xff, writer->f); + fputc((height >> 8) & 0xff, writer->f); + + fputc(0xf0, writer->f); // there is an unsorted global color table of 2 entries + fputc(0, writer->f); // background color + fputc(0, writer->f); // pixels are square (we need to specify this because it's 1989) + + // now the "global" palette (really just a dummy palette) + // color 0: black + fputc(0, writer->f); + fputc(0, writer->f); + fputc(0, writer->f); + // color 1: also black + fputc(0, writer->f); + fputc(0, writer->f); + fputc(0, writer->f); + + if( delay != 0 ) + { + // animation header + fputc(0x21, writer->f); // extension + fputc(0xff, writer->f); // application specific + fputc(11, writer->f); // length 11 + fputs("NETSCAPE2.0", writer->f); // yes, really + fputc(3, writer->f); // 3 bytes of NETSCAPE2.0 data + + fputc(1, writer->f); // JUST BECAUSE + fputc(0, writer->f); // loop infinitely (byte 0) + fputc(0, writer->f); // loop infinitely (byte 1) + + fputc(0, writer->f); // block terminator + } + + return true; +} + +// Writes out a new frame to a GIF in progress. +// The GIFWriter should have been created by GIFBegin. +// AFAIK, it is legal to use different bit depths for different frames of an image - +// this may be handy to save bits in animations that don't change much. +bool GifWriteFrame( GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, int bitDepth = 8, bool dither = false ) +{ + if(!writer->f) return false; + + const uint8_t* oldImage = writer->firstFrame? NULL : writer->oldImage; + writer->firstFrame = false; + + GifPalette pal; + GifMakePalette((dither? NULL : oldImage), image, width, height, bitDepth, dither, &pal); + + if(dither) + GifDitherImage(oldImage, image, writer->oldImage, width, height, &pal); + else + GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal); + + GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal); + + return true; +} + +// Writes the EOF code, closes the file handle, and frees temp memory used by a GIF. +// Many if not most viewers will still display a GIF properly if the EOF code is missing, +// but it's still a good idea to write it out. +bool GifEnd( GifWriter* writer ) +{ + if(!writer->f) return false; + + fputc(0x3b, writer->f); // end of file + fclose(writer->f); + GIF_FREE(writer->oldImage); + + writer->f = NULL; + writer->oldImage = NULL; + + return true; +} + +#endif diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 54a98c42..01ba52c7 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -58,6 +58,7 @@ #include "main.h" #include "Input.h" #include "CheatsDialog.h" +#include "DateTimeDialog.h" #include "EmuSettingsDialog.h" #include "InputConfig/InputConfigDialog.h" #include "VideoSettingsDialog.h" @@ -78,8 +79,8 @@ #include "version.h" #include "FrontendUtil.h" -#include "OSD.h" +#include "Args.h" #include "NDS.h" #include "NDSCart.h" #include "GBACart.h" @@ -89,11 +90,15 @@ #include "Platform.h" #include "LocalMP.h" #include "Config.h" +#include "RTC.h" +#include "DSi.h" #include "DSi_I2C.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" #include "Savestate.h" -#include "main_shaders.h" +//#include "main_shaders.h" #include "ROMManager.h" #include "ArchiveUtil.h" @@ -102,15 +107,17 @@ #include "CLI.h" // TODO: uniform variable spelling +using namespace melonDS; +QString NdsRomMimeType = "application/x-nintendo-ds-rom"; +QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; -const QString NdsRomMimeType = "application/x-nintendo-ds-rom"; -const QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; +QString GbaRomMimeType = "application/x-gba-rom"; +QStringList GbaRomExtensions { ".gba", ".agb" }; -const QString GbaRomMimeType = "application/x-gba-rom"; -const QStringList GbaRomExtensions { ".gba", ".agb" }; +QString* systemThemeName; // This list of supported archive formats is based on libarchive(3) version 3.6.2 (2022-12-09). -const QStringList ArchiveMimeTypes +QStringList ArchiveMimeTypes { #ifdef ARCHIVE_SUPPORT_ENABLED "application/zip", @@ -132,7 +139,7 @@ const QStringList ArchiveMimeTypes #endif }; -const QStringList ArchiveExtensions +QStringList ArchiveExtensions { #ifdef ARCHIVE_SUPPORT_ENABLED ".zip", ".7z", ".rar", ".tar", @@ -160,1129 +167,16 @@ EmuThread* emuThread; int autoScreenSizing = 0; int videoRenderer; -GPU::RenderSettings videoSettings; bool videoSettingsDirty; CameraManager* camManager[2]; bool camStarted[2]; -const struct { int id; float ratio; const char* label; } aspectRatios[] = -{ - { 0, 1, "4:3 (native)" }, - { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, - { 1, (16.f / 9) / (4.f / 3), "16:9" }, - { 2, (21.f / 9) / (4.f / 3), "21:9" }, - { 3, 0, "window" } -}; -constexpr int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]); +//extern int AspectRatiosNum; -EmuThread::EmuThread(QObject* parent) : QThread(parent) -{ - EmuStatus = emuStatus_Exit; - EmuRunning = emuStatus_Paused; - EmuPauseStack = EmuPauseStackRunning; - RunningSomething = false; - connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint())); - connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); - connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); - connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); - connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); - connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); - connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); - connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); - connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged())); - connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); - connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); - connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); - static_cast(mainWindow->panel)->transferLayout(this); -} - -void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix) -{ - screenSettingsLock.lock(); - - if (lastScreenWidth != windowInfo.surface_width || lastScreenHeight != windowInfo.surface_height) - { - if (oglContext) - oglContext->ResizeSurface(windowInfo.surface_width, windowInfo.surface_height); - lastScreenWidth = windowInfo.surface_width; - lastScreenHeight = windowInfo.surface_height; - } - - this->filter = filter; - this->windowInfo = windowInfo; - this->numScreens = numScreens; - memcpy(this->screenKind, screenKind, sizeof(int)*numScreens); - memcpy(this->screenMatrix, screenMatrix, sizeof(float)*numScreens*6); - - screenSettingsLock.unlock(); -} - -void EmuThread::initOpenGL() -{ - GL::Context* windowctx = mainWindow->getOGLContext(); - - oglContext = windowctx; - oglContext->MakeCurrent(); - - OpenGL::CompileVertexFragmentProgram(screenShaderProgram, - kScreenVS, kScreenFS, - "ScreenShader", - {{"vPosition", 0}, {"vTexcoord", 1}}, - {{"oColor", 0}}); - - glUseProgram(screenShaderProgram); - glUniform1i(glGetUniformLocation(screenShaderProgram, "ScreenTex"), 0); - - screenShaderScreenSizeULoc = glGetUniformLocation(screenShaderProgram, "uScreenSize"); - screenShaderTransformULoc = glGetUniformLocation(screenShaderProgram, "uTransform"); - - // to prevent bleeding between both parts of the screen - // with bilinear filtering enabled - const int paddedHeight = 192*2+2; - const float padPixels = 1.f / paddedHeight; - - const float vertices[] = - { - 0.f, 0.f, 0.f, 0.f, - 0.f, 192.f, 0.f, 0.5f - padPixels, - 256.f, 192.f, 1.f, 0.5f - padPixels, - 0.f, 0.f, 0.f, 0.f, - 256.f, 192.f, 1.f, 0.5f - padPixels, - 256.f, 0.f, 1.f, 0.f, - - 0.f, 0.f, 0.f, 0.5f + padPixels, - 0.f, 192.f, 0.f, 1.f, - 256.f, 192.f, 1.f, 1.f, - 0.f, 0.f, 0.f, 0.5f + padPixels, - 256.f, 192.f, 1.f, 1.f, - 256.f, 0.f, 1.f, 0.5f + padPixels - }; - - glGenBuffers(1, &screenVertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glGenVertexArrays(1, &screenVertexArray); - glBindVertexArray(screenVertexArray); - glEnableVertexAttribArray(0); // position - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); - glEnableVertexAttribArray(1); // texcoord - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); - - glGenTextures(1, &screenTexture); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, screenTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - // fill the padding - u8 zeroData[256*4*4]; - memset(zeroData, 0, sizeof(zeroData)); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); - - OSD::Init(true); - - oglContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); -} - -void EmuThread::deinitOpenGL() -{ - glDeleteTextures(1, &screenTexture); - - glDeleteVertexArrays(1, &screenVertexArray); - glDeleteBuffers(1, &screenVertexBuffer); - - glDeleteProgram(screenShaderProgram); - - OSD::DeInit(); - - oglContext->DoneCurrent(); - oglContext = nullptr; - - lastScreenWidth = lastScreenHeight = -1; -} - -void EmuThread::run() -{ - u32 mainScreenPos[3]; - - NDS::Init(); - - mainScreenPos[0] = 0; - mainScreenPos[1] = 0; - mainScreenPos[2] = 0; - autoScreenSizing = 0; - - videoSettingsDirty = false; - videoSettings.Soft_Threaded = Config::Threaded3D != 0; - videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; - videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; - videoSettings.GL_HiresCoordinates = Config::GL_HiresCoordinates; - - if (mainWindow->hasOGL) - { - initOpenGL(); - videoRenderer = Config::_3DRenderer; - } - else - { - videoRenderer = 0; - } - - GPU::InitRenderer(videoRenderer); - GPU::SetRenderSettings(videoRenderer, videoSettings); - - SPU::SetInterpolation(Config::AudioInterp); - - Input::Init(); - - u32 nframes = 0; - double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency(); - double lastTime = SDL_GetPerformanceCounter() * perfCountsSec; - double frameLimitError = 0.0; - double lastMeasureTime = lastTime; - - u32 winUpdateCount = 0, winUpdateFreq = 1; - u8 dsiVolumeLevel = 0x1F; - - char melontitle[100]; - - while (EmuRunning != emuStatus_Exit) - { - Input::Process(); - - if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); - - if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause(); - if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset(); - if (Input::HotkeyPressed(HK_FrameStep)) emit windowEmuFrameStep(); - - if (Input::HotkeyPressed(HK_FullscreenToggle)) emit windowFullscreenToggle(); - - if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle(); - if (Input::HotkeyPressed(HK_SwapScreenEmphasis)) emit screenEmphasisToggle(); - - if (Input::HotkeyPressed(HK_SolarSensorDecrease)) - { - int level = GBACart::SetInput(GBACart::Input_SolarSensorDown, true); - if (level != -1) - { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); - } - } - if (Input::HotkeyPressed(HK_SolarSensorIncrease)) - { - int level = GBACart::SetInput(GBACart::Input_SolarSensorUp, true); - if (level != -1) - { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); - } - } - - if (NDS::ConsoleType == 1) - { - double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; - - // Handle power button - if (Input::HotkeyDown(HK_PowerButton)) - { - DSi_BPTWL::SetPowerButtonHeld(currentTime); - } - else if (Input::HotkeyReleased(HK_PowerButton)) - { - DSi_BPTWL::SetPowerButtonReleased(currentTime); - } - - // Handle volume buttons - if (Input::HotkeyDown(HK_VolumeUp)) - { - DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); - } - else if (Input::HotkeyReleased(HK_VolumeUp)) - { - DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); - } - - if (Input::HotkeyDown(HK_VolumeDown)) - { - DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); - } - else if (Input::HotkeyReleased(HK_VolumeDown)) - { - DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); - } - - DSi_BPTWL::ProcessVolumeSwitchInput(currentTime); - } - - if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) - { - EmuStatus = emuStatus_Running; - if (EmuRunning == emuStatus_FrameStep) EmuRunning = emuStatus_Paused; - - // update render settings if needed - // HACK: - // once the fast forward hotkey is released, we need to update vsync - // to the old setting again - if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward)) - { - if (oglContext) - { - oglContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); - videoRenderer = Config::_3DRenderer; - } -#ifdef OGLRENDERER_ENABLED - else -#endif - { - videoRenderer = GPU::renderer3D_Software; - } - - videoRenderer = oglContext ? Config::_3DRenderer : GPU::renderer3D_Software; - - videoSettingsDirty = false; - - videoSettings.Soft_Threaded = Config::Threaded3D != 0; - videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; - videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; - videoSettings.GL_HiresCoordinates = Config::GL_HiresCoordinates; - - GPU::SetRenderSettings(videoRenderer, videoSettings); - } - - // process input and hotkeys - NDS::SetKeyMask(Input::InputMask); - - if (Input::HotkeyPressed(HK_Lid)) - { - bool lid = !NDS::IsLidClosed(); - NDS::SetLidClosed(lid); - OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); - } - - // microphone input - AudioInOut::MicProcess(); - - // auto screen layout - if (Config::ScreenSizing == Frontend::screenSizing_Auto) - { - mainScreenPos[2] = mainScreenPos[1]; - mainScreenPos[1] = mainScreenPos[0]; - mainScreenPos[0] = NDS::PowerControl9 >> 15; - - int guess; - if (mainScreenPos[0] == mainScreenPos[2] && - mainScreenPos[0] != mainScreenPos[1]) - { - // constant flickering, likely displaying 3D on both screens - // TODO: when both screens are used for 2D only...??? - guess = Frontend::screenSizing_Even; - } - else - { - if (mainScreenPos[0] == 1) - guess = Frontend::screenSizing_EmphTop; - else - guess = Frontend::screenSizing_EmphBot; - } - - if (guess != autoScreenSizing) - { - autoScreenSizing = guess; - emit screenLayoutChange(); - } - } - - - // emulate - u32 nlines = NDS::RunFrame(); - - if (ROMManager::NDSSave) - ROMManager::NDSSave->CheckFlush(); - - if (ROMManager::GBASave) - ROMManager::GBASave->CheckFlush(); - - if (ROMManager::FirmwareSave) - ROMManager::FirmwareSave->CheckFlush(); - - if (!oglContext) - { - FrontBufferLock.lock(); - FrontBuffer = GPU::FrontBuffer; - FrontBufferLock.unlock(); - } - else - { - FrontBuffer = GPU::FrontBuffer; - drawScreenGL(); - } - -#ifdef MELONCAP - MelonCap::Update(); -#endif // MELONCAP - - if (EmuRunning == emuStatus_Exit) break; - - winUpdateCount++; - if (winUpdateCount >= winUpdateFreq && !oglContext) - { - emit windowUpdate(); - winUpdateCount = 0; - } - - bool fastforward = Input::HotkeyDown(HK_FastForward); - - if (fastforward && oglContext && Config::ScreenVSync) - { - oglContext->SetSwapInterval(0); - } - - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) - { - u8 volumeLevel = DSi_BPTWL::GetVolumeLevel(); - if (volumeLevel != dsiVolumeLevel) - { - dsiVolumeLevel = volumeLevel; - emit syncVolumeLevel(); - } - - Config::AudioVolume = volumeLevel * (256.0 / 31.0); - } - - if (Config::AudioSync && !fastforward) - AudioInOut::AudioSync(); - - double frametimeStep = nlines / (60.0 * 263.0); - - { - bool limitfps = Config::LimitFPS && !fastforward; - - double practicalFramelimit = limitfps ? frametimeStep : 1.0 / 1000.0; - - double curtime = SDL_GetPerformanceCounter() * perfCountsSec; - - frameLimitError += practicalFramelimit - (curtime - lastTime); - if (frameLimitError < -practicalFramelimit) - frameLimitError = -practicalFramelimit; - if (frameLimitError > practicalFramelimit) - frameLimitError = practicalFramelimit; - - if (round(frameLimitError * 1000.0) > 0.0) - { - SDL_Delay(round(frameLimitError * 1000.0)); - double timeBeforeSleep = curtime; - curtime = SDL_GetPerformanceCounter() * perfCountsSec; - frameLimitError -= curtime - timeBeforeSleep; - } - - lastTime = curtime; - } - - nframes++; - if (nframes >= 30) - { - double time = SDL_GetPerformanceCounter() * perfCountsSec; - double dt = time - lastMeasureTime; - lastMeasureTime = time; - - u32 fps = round(nframes / dt); - nframes = 0; - - float fpstarget = 1.0/frametimeStep; - - winUpdateFreq = fps / (u32)round(fpstarget); - if (winUpdateFreq < 1) - winUpdateFreq = 1; - - int inst = Platform::InstanceID(); - if (inst == 0) - sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); - else - sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); - changeWindowTitle(melontitle); - } - } - else - { - // paused - nframes = 0; - lastTime = SDL_GetPerformanceCounter() * perfCountsSec; - lastMeasureTime = lastTime; - - emit windowUpdate(); - - EmuStatus = EmuRunning; - - int inst = Platform::InstanceID(); - if (inst == 0) - sprintf(melontitle, "melonDS " MELONDS_VERSION); - else - sprintf(melontitle, "melonDS (%d)", inst+1); - changeWindowTitle(melontitle); - - SDL_Delay(75); - - if (oglContext) - drawScreenGL(); - - ContextRequestKind contextRequest = ContextRequest; - if (contextRequest == contextRequest_InitGL) - { - initOpenGL(); - ContextRequest = contextRequest_None; - } - else if (contextRequest == contextRequest_DeInitGL) - { - deinitOpenGL(); - ContextRequest = contextRequest_None; - } - } - } - - EmuStatus = emuStatus_Exit; - - GPU::DeInitRenderer(); - NDS::DeInit(); - //Platform::LAN_DeInit(); -} - -void EmuThread::changeWindowTitle(char* title) -{ - emit windowTitleChange(QString(title)); -} - -void EmuThread::emuRun() -{ - EmuRunning = emuStatus_Running; - EmuPauseStack = EmuPauseStackRunning; - RunningSomething = true; - - // checkme - emit windowEmuStart(); - AudioInOut::Enable(); -} - -void EmuThread::initContext() -{ - ContextRequest = contextRequest_InitGL; - while (ContextRequest != contextRequest_None); -} - -void EmuThread::deinitContext() -{ - ContextRequest = contextRequest_DeInitGL; - while (ContextRequest != contextRequest_None); -} - -void EmuThread::emuPause() -{ - EmuPauseStack++; - if (EmuPauseStack > EmuPauseStackPauseThreshold) return; - - PrevEmuStatus = EmuRunning; - EmuRunning = emuStatus_Paused; - while (EmuStatus != emuStatus_Paused); - - AudioInOut::Disable(); -} - -void EmuThread::emuUnpause() -{ - if (EmuPauseStack < EmuPauseStackPauseThreshold) return; - - EmuPauseStack--; - if (EmuPauseStack >= EmuPauseStackPauseThreshold) return; - - EmuRunning = PrevEmuStatus; - - AudioInOut::Enable(); -} - -void EmuThread::emuStop() -{ - EmuRunning = emuStatus_Exit; - EmuPauseStack = EmuPauseStackRunning; - - AudioInOut::Disable(); -} - -void EmuThread::emuFrameStep() -{ - if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause(); - EmuRunning = emuStatus_FrameStep; -} - -bool EmuThread::emuIsRunning() -{ - return EmuRunning == emuStatus_Running; -} - -bool EmuThread::emuIsActive() -{ - return (RunningSomething == 1); -} - -void EmuThread::drawScreenGL() -{ - int w = windowInfo.surface_width; - int h = windowInfo.surface_height; - float factor = windowInfo.surface_scale; - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDisable(GL_DEPTH_TEST); - glDepthMask(false); - glDisable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_STENCIL_TEST); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, w, h); - - glUseProgram(screenShaderProgram); - glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); - - int frontbuf = FrontBuffer; - glActiveTexture(GL_TEXTURE0); - -#ifdef OGLRENDERER_ENABLED - if (GPU::Renderer != 0) - { - // hardware-accelerated render - GPU::CurGLCompositor->BindOutputTexture(frontbuf); - } - else -#endif - { - // regular render - glBindTexture(GL_TEXTURE_2D, screenTexture); - - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) - { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); - } - } - - screenSettingsLock.lock(); - - GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBindVertexArray(screenVertexArray); - - for (int i = 0; i < numScreens; i++) - { - glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); - glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); - } - - screenSettingsLock.unlock(); - - OSD::Update(); - OSD::DrawGL(w, h); - - oglContext->SwapBuffers(); -} - -ScreenHandler::ScreenHandler(QWidget* widget) -{ - widget->setMouseTracking(true); - widget->setAttribute(Qt::WA_AcceptTouchEvents); - QTimer* mouseTimer = setupMouseTimer(); - widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);}); -} - -ScreenHandler::~ScreenHandler() -{ - mouseTimer->stop(); -} - -void ScreenHandler::screenSetupLayout(int w, int h) -{ - int sizing = Config::ScreenSizing; - if (sizing == 3) sizing = autoScreenSizing; - - float aspectTop, aspectBot; - - for (auto ratio : aspectRatios) - { - if (ratio.id == Config::ScreenAspectTop) - aspectTop = ratio.ratio; - if (ratio.id == Config::ScreenAspectBot) - aspectBot = ratio.ratio; - } - - if (aspectTop == 0) - aspectTop = ((float) w / h) / (4.f / 3.f); - - if (aspectBot == 0) - aspectBot = ((float) w / h) / (4.f / 3.f); - - Frontend::SetupScreenLayout(w, h, - static_cast(Config::ScreenLayout), - static_cast(Config::ScreenRotation), - static_cast(sizing), - Config::ScreenGap, - Config::IntegerScaling != 0, - Config::ScreenSwap != 0, - aspectTop, - aspectBot); - - numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); -} - -QSize ScreenHandler::screenGetMinSize(int factor = 1) -{ - bool isHori = (Config::ScreenRotation == Frontend::screenRot_90Deg - || Config::ScreenRotation == Frontend::screenRot_270Deg); - int gap = Config::ScreenGap * factor; - - int w = 256 * factor; - int h = 192 * factor; - - if (Config::ScreenSizing == Frontend::screenSizing_TopOnly - || Config::ScreenSizing == Frontend::screenSizing_BotOnly) - { - return QSize(w, h); - } - - if (Config::ScreenLayout == Frontend::screenLayout_Natural) - { - if (isHori) - return QSize(h+gap+h, w); - else - return QSize(w, h+gap+h); - } - else if (Config::ScreenLayout == Frontend::screenLayout_Vertical) - { - if (isHori) - return QSize(h, w+gap+w); - else - return QSize(w, h+gap+h); - } - else if (Config::ScreenLayout == Frontend::screenLayout_Horizontal) - { - if (isHori) - return QSize(h+gap+h, w); - else - return QSize(w+gap+w, h); - } - else // hybrid - { - if (isHori) - return QSize(h+gap+h, 3*w + (int)ceil((4*gap) / 3.0)); - else - return QSize(3*w + (int)ceil((4*gap) / 3.0), h+gap+h); - } -} - -void ScreenHandler::screenOnMousePress(QMouseEvent* event) -{ - event->accept(); - if (event->button() != Qt::LeftButton) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - if (Frontend::GetTouchCoords(x, y, false)) - { - touching = true; - NDS::TouchScreen(x, y); - } -} - -void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) -{ - event->accept(); - if (event->button() != Qt::LeftButton) return; - - if (touching) - { - touching = false; - NDS::ReleaseScreen(); - } -} - -void ScreenHandler::screenOnMouseMove(QMouseEvent* event) -{ - event->accept(); - - showCursor(); - - if (!(event->buttons() & Qt::LeftButton)) return; - if (!touching) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - if (Frontend::GetTouchCoords(x, y, true)) - NDS::TouchScreen(x, y); -} - -void ScreenHandler::screenHandleTablet(QTabletEvent* event) -{ - event->accept(); - - switch(event->type()) - { - case QEvent::TabletPress: - case QEvent::TabletMove: - { - int x = event->x(); - int y = event->y(); - - if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) - { - touching = true; - NDS::TouchScreen(x, y); - } - } - break; - case QEvent::TabletRelease: - if (touching) - { - NDS::ReleaseScreen(); - touching = false; - } - break; - default: - break; - } -} - -void ScreenHandler::screenHandleTouch(QTouchEvent* event) -{ - event->accept(); - - switch(event->type()) - { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - if (event->touchPoints().length() > 0) - { - QPointF lastPosition = event->touchPoints().first().lastPos(); - int x = (int)lastPosition.x(); - int y = (int)lastPosition.y(); - - if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) - { - touching = true; - NDS::TouchScreen(x, y); - } - } - break; - case QEvent::TouchEnd: - if (touching) - { - NDS::ReleaseScreen(); - touching = false; - } - break; - default: - break; - } -} - -void ScreenHandler::showCursor() -{ - mainWindow->panelWidget->setCursor(Qt::ArrowCursor); - mouseTimer->start(); -} - -QTimer* ScreenHandler::setupMouseTimer() -{ - mouseTimer = new QTimer(); - mouseTimer->setSingleShot(true); - mouseTimer->setInterval(Config::MouseHideSeconds*1000); - mouseTimer->start(); - - return mouseTimer; -} - -ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this) -{ - screen[0] = QImage(256, 192, QImage::Format_RGB32); - screen[1] = QImage(256, 192, QImage::Format_RGB32); - - screenTrans[0].reset(); - screenTrans[1].reset(); - - OSD::Init(false); -} - -ScreenPanelNative::~ScreenPanelNative() -{ - OSD::DeInit(); -} - -void ScreenPanelNative::setupScreenLayout() -{ - int w = width(); - int h = height(); - - screenSetupLayout(w, h); - - for (int i = 0; i < numScreens; i++) - { - float* mtx = screenMatrix[i]; - screenTrans[i].setMatrix(mtx[0], mtx[1], 0.f, - mtx[2], mtx[3], 0.f, - mtx[4], mtx[5], 1.f); - } -} - -void ScreenPanelNative::paintEvent(QPaintEvent* event) -{ - QPainter painter(this); - - // fill background - painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); - - if (emuThread->emuIsActive()) - { - emuThread->FrontBufferLock.lock(); - int frontbuf = emuThread->FrontBuffer; - if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) - { - emuThread->FrontBufferLock.unlock(); - return; - } - - memcpy(screen[0].scanLine(0), GPU::Framebuffer[frontbuf][0], 256 * 192 * 4); - memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256 * 192 * 4); - emuThread->FrontBufferLock.unlock(); - - QRect screenrc(0, 0, 256, 192); - - for (int i = 0; i < numScreens; i++) - { - painter.setTransform(screenTrans[i]); - painter.drawImage(screenrc, screen[screenKind[i]]); - } - } - - OSD::Update(); - OSD::DrawNative(painter); -} - -void ScreenPanelNative::resizeEvent(QResizeEvent* event) -{ - setupScreenLayout(); -} - -void ScreenPanelNative::mousePressEvent(QMouseEvent* event) -{ - screenOnMousePress(event); -} - -void ScreenPanelNative::mouseReleaseEvent(QMouseEvent* event) -{ - screenOnMouseRelease(event); -} - -void ScreenPanelNative::mouseMoveEvent(QMouseEvent* event) -{ - screenOnMouseMove(event); -} - -void ScreenPanelNative::tabletEvent(QTabletEvent* event) -{ - screenHandleTablet(event); -} - -bool ScreenPanelNative::event(QEvent* event) -{ - if (event->type() == QEvent::TouchBegin - || event->type() == QEvent::TouchEnd - || event->type() == QEvent::TouchUpdate) - { - screenHandleTouch((QTouchEvent*)event); - return true; - } - return QWidget::event(event); -} - -void ScreenPanelNative::onScreenLayoutChanged() -{ - setMinimumSize(screenGetMinSize()); - setupScreenLayout(); -} - - -ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QWidget(parent), ScreenHandler(this) -{ - setAutoFillBackground(false); - setAttribute(Qt::WA_NativeWindow, true); - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_PaintOnScreen, true); - setAttribute(Qt::WA_KeyCompression, false); - setFocusPolicy(Qt::StrongFocus); - setMinimumSize(screenGetMinSize()); -} - -ScreenPanelGL::~ScreenPanelGL() -{} - -bool ScreenPanelGL::createContext() -{ - std::optional windowInfo = getWindowInfo(); - std::array versionsToTry = { - GL::Context::Version{GL::Context::Profile::Core, 4, 3}, - GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; - if (windowInfo.has_value()) - { - glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); - glContext->DoneCurrent(); - } - - return glContext != nullptr; -} - -qreal ScreenPanelGL::devicePixelRatioFromScreen() const -{ - const QScreen* screen_for_ratio = window()->windowHandle()->screen(); - if (!screen_for_ratio) - screen_for_ratio = QGuiApplication::primaryScreen(); - - return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); -} - -int ScreenPanelGL::scaledWindowWidth() const -{ - return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); -} - -int ScreenPanelGL::scaledWindowHeight() const -{ - return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); -} - -std::optional ScreenPanelGL::getWindowInfo() -{ - WindowInfo wi; - - // Windows and Apple are easy here since there's no display connection. - #if defined(_WIN32) - wi.type = WindowInfo::Type::Win32; - wi.window_handle = reinterpret_cast(winId()); - #elif defined(__APPLE__) - wi.type = WindowInfo::Type::MacOS; - wi.window_handle = reinterpret_cast(winId()); - #else - QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); - const QString platform_name = QGuiApplication::platformName(); - if (platform_name == QStringLiteral("xcb")) - { - wi.type = WindowInfo::Type::X11; - wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); - wi.window_handle = reinterpret_cast(winId()); - } - else if (platform_name == QStringLiteral("wayland")) - { - wi.type = WindowInfo::Type::Wayland; - QWindow* handle = windowHandle(); - if (handle == nullptr) - return std::nullopt; - - wi.display_connection = pni->nativeResourceForWindow("display", handle); - wi.window_handle = pni->nativeResourceForWindow("surface", handle); - } - else - { - qCritical() << "Unknown PNI platform " << platform_name; - return std::nullopt; - } - #endif - - wi.surface_width = static_cast(scaledWindowWidth()); - wi.surface_height = static_cast(scaledWindowHeight()); - wi.surface_scale = static_cast(devicePixelRatioFromScreen()); - - return wi; -} - - -QPaintEngine* ScreenPanelGL::paintEngine() const -{ - return nullptr; -} - -void ScreenPanelGL::setupScreenLayout() -{ - int w = width(); - int h = height(); - - screenSetupLayout(w, h); - if (emuThread) - transferLayout(emuThread); -} - -void ScreenPanelGL::resizeEvent(QResizeEvent* event) -{ - setupScreenLayout(); - - QWidget::resizeEvent(event); -} - -void ScreenPanelGL::mousePressEvent(QMouseEvent* event) -{ - screenOnMousePress(event); -} - -void ScreenPanelGL::mouseReleaseEvent(QMouseEvent* event) -{ - screenOnMouseRelease(event); -} - -void ScreenPanelGL::mouseMoveEvent(QMouseEvent* event) -{ - screenOnMouseMove(event); -} - -void ScreenPanelGL::tabletEvent(QTabletEvent* event) -{ - screenHandleTablet(event); -} - -bool ScreenPanelGL::event(QEvent* event) -{ - if (event->type() == QEvent::TouchBegin - || event->type() == QEvent::TouchEnd - || event->type() == QEvent::TouchUpdate) - { - screenHandleTouch((QTouchEvent*)event); - return true; - } - return QWidget::event(event); -} - -void ScreenPanelGL::transferLayout(EmuThread* thread) -{ - std::optional windowInfo = getWindowInfo(); - if (windowInfo.has_value()) - thread->updateScreenSettings(Config::ScreenFilter, *windowInfo, numScreens, screenKind, &screenMatrix[0][0]); -} - -void ScreenPanelGL::onScreenLayoutChanged() -{ - setMinimumSize(screenGetMinSize()); - setupScreenLayout(); -} static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs = Qt::CaseInsensitive) @@ -1357,1843 +251,7 @@ static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive } -#ifndef _WIN32 -static int signalFd[2]; -QSocketNotifier *signalSn; -static void signalHandler(int) -{ - char a = 1; - write(signalFd[0], &a, sizeof(a)); -} -#endif - -MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) -{ -#ifndef _WIN32 - if (socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) - { - qFatal("Couldn't create socketpair"); - } - - signalSn = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); - connect(signalSn, SIGNAL(activated(int)), this, SLOT(onQuit())); - - struct sigaction sa; - - sa.sa_handler = signalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_flags |= SA_RESTART; - sigaction(SIGINT, &sa, 0); -#endif - - oldW = Config::WindowWidth; - oldH = Config::WindowHeight; - oldMax = Config::WindowMaximized; - - setWindowTitle("melonDS " MELONDS_VERSION); - setAttribute(Qt::WA_DeleteOnClose); - setAcceptDrops(true); - setFocusPolicy(Qt::ClickFocus); - - int inst = Platform::InstanceID(); - - QMenuBar* menubar = new QMenuBar(); - { - QMenu* menu = menubar->addMenu("File"); - - actOpenROM = menu->addAction("Open ROM..."); - connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); - actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); - - /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); - connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); - actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ - - recentMenu = menu->addMenu("Open recent"); - for (int i = 0; i < 10; ++i) - { - std::string item = Config::RecentROMList[i]; - if (!item.empty()) - recentFileList.push_back(QString::fromStdString(item)); - } - updateRecentFilesMenu(); - - //actBootFirmware = menu->addAction("Launch DS menu"); - actBootFirmware = menu->addAction("Boot firmware"); - connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); - - menu->addSeparator(); - - actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel()); - actCurrentCart->setEnabled(false); - - actInsertCart = menu->addAction("Insert cart..."); - connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); - - actEjectCart = menu->addAction("Eject cart"); - connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); - - menu->addSeparator(); - - actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel()); - actCurrentGBACart->setEnabled(false); - - actInsertGBACart = menu->addAction("Insert ROM cart..."); - connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); - - { - QMenu* submenu = menu->addMenu("Insert add-on cart"); - - actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); - actInsertGBAAddon[0]->setData(QVariant(NDS::GBAAddon_RAMExpansion)); - connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); - } - - actEjectGBACart = menu->addAction("Eject cart"); - connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); - - menu->addSeparator(); - - actImportSavefile = menu->addAction("Import savefile"); - connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); - - menu->addSeparator(); - - { - QMenu* submenu = menu->addMenu("Save state"); - - for (int i = 1; i < 9; i++) - { - actSaveState[i] = submenu->addAction(QString("%1").arg(i)); - actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); - actSaveState[i]->setData(QVariant(i)); - connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); - } - - actSaveState[0] = submenu->addAction("File..."); - actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); - actSaveState[0]->setData(QVariant(0)); - connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); - } - { - QMenu* submenu = menu->addMenu("Load state"); - - for (int i = 1; i < 9; i++) - { - actLoadState[i] = submenu->addAction(QString("%1").arg(i)); - actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); - actLoadState[i]->setData(QVariant(i)); - connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); - } - - actLoadState[0] = submenu->addAction("File..."); - actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); - actLoadState[0]->setData(QVariant(0)); - connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); - } - - actUndoStateLoad = menu->addAction("Undo state load"); - actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); - connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); - - menu->addSeparator(); - - actQuit = menu->addAction("Quit"); - connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); - actQuit->setShortcut(QKeySequence(QKeySequence::StandardKey::Quit)); - } - { - QMenu* menu = menubar->addMenu("System"); - - actPause = menu->addAction("Pause"); - actPause->setCheckable(true); - connect(actPause, &QAction::triggered, this, &MainWindow::onPause); - - actReset = menu->addAction("Reset"); - connect(actReset, &QAction::triggered, this, &MainWindow::onReset); - - actStop = menu->addAction("Stop"); - connect(actStop, &QAction::triggered, this, &MainWindow::onStop); - - actFrameStep = menu->addAction("Frame step"); - connect(actFrameStep, &QAction::triggered, this, &MainWindow::onFrameStep); - - menu->addSeparator(); - - actPowerManagement = menu->addAction("Power management"); - connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); - - menu->addSeparator(); - - actEnableCheats = menu->addAction("Enable cheats"); - actEnableCheats->setCheckable(true); - connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); - - //if (inst == 0) - { - actSetupCheats = menu->addAction("Setup cheat codes"); - actSetupCheats->setMenuRole(QAction::NoRole); - connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); - - menu->addSeparator(); - actROMInfo = menu->addAction("ROM info"); - connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); - - actRAMInfo = menu->addAction("RAM search"); - connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); - - actTitleManager = menu->addAction("Manage DSi titles"); - connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); - } - - { - menu->addSeparator(); - QMenu* submenu = menu->addMenu("Multiplayer"); - - actMPNewInstance = submenu->addAction("Launch new instance"); - connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); - } - } - { - QMenu* menu = menubar->addMenu("Config"); - - actEmuSettings = menu->addAction("Emu settings"); - connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - -#ifdef __APPLE__ - actPreferences = menu->addAction("Preferences..."); - connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - actPreferences->setMenuRole(QAction::PreferencesRole); -#endif - - actInputConfig = menu->addAction("Input and hotkeys"); - connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); - - actVideoSettings = menu->addAction("Video settings"); - connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); - - actCameraSettings = menu->addAction("Camera settings"); - connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); - - actAudioSettings = menu->addAction("Audio settings"); - connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); - - actMPSettings = menu->addAction("Multiplayer settings"); - connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); - - actWifiSettings = menu->addAction("Wifi settings"); - connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); - - actFirmwareSettings = menu->addAction("Firmware settings"); - connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); - - actInterfaceSettings = menu->addAction("Interface settings"); - connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); - - actPathSettings = menu->addAction("Path settings"); - connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); - - { - QMenu* submenu = menu->addMenu("Savestate settings"); - - actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); - actSavestateSRAMReloc->setCheckable(true); - connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); - } - - menu->addSeparator(); - - { - QMenu* submenu = menu->addMenu("Screen size"); - - for (int i = 0; i < 4; i++) - { - int data = i+1; - actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); - actScreenSize[i]->setData(QVariant(data)); - connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); - } - } - { - QMenu* submenu = menu->addMenu("Screen rotation"); - grpScreenRotation = new QActionGroup(submenu); - - for (int i = 0; i < Frontend::screenRot_MAX; i++) - { - int data = i*90; - actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); - actScreenRotation[i]->setActionGroup(grpScreenRotation); - actScreenRotation[i]->setData(QVariant(i)); - actScreenRotation[i]->setCheckable(true); - } - - connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); - } - { - QMenu* submenu = menu->addMenu("Screen gap"); - grpScreenGap = new QActionGroup(submenu); - - const int screengap[] = {0, 1, 8, 64, 90, 128}; - - for (int i = 0; i < 6; i++) - { - int data = screengap[i]; - actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); - actScreenGap[i]->setActionGroup(grpScreenGap); - actScreenGap[i]->setData(QVariant(data)); - actScreenGap[i]->setCheckable(true); - } - - connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); - } - { - QMenu* submenu = menu->addMenu("Screen layout"); - grpScreenLayout = new QActionGroup(submenu); - - const char* screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; - - for (int i = 0; i < Frontend::screenLayout_MAX; i++) - { - actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); - actScreenLayout[i]->setActionGroup(grpScreenLayout); - actScreenLayout[i]->setData(QVariant(i)); - actScreenLayout[i]->setCheckable(true); - } - - connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); - - submenu->addSeparator(); - - actScreenSwap = submenu->addAction("Swap screens"); - actScreenSwap->setCheckable(true); - connect(actScreenSwap, &QAction::triggered, this, &MainWindow::onChangeScreenSwap); - } - { - QMenu* submenu = menu->addMenu("Screen sizing"); - grpScreenSizing = new QActionGroup(submenu); - - const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; - - for (int i = 0; i < Frontend::screenSizing_MAX; i++) - { - actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); - actScreenSizing[i]->setActionGroup(grpScreenSizing); - actScreenSizing[i]->setData(QVariant(i)); - actScreenSizing[i]->setCheckable(true); - } - - connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); - - submenu->addSeparator(); - - actIntegerScaling = submenu->addAction("Force integer scaling"); - actIntegerScaling->setCheckable(true); - connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); - } - { - QMenu* submenu = menu->addMenu("Aspect ratio"); - grpScreenAspectTop = new QActionGroup(submenu); - grpScreenAspectBot = new QActionGroup(submenu); - actScreenAspectTop = new QAction*[AspectRatiosNum]; - actScreenAspectBot = new QAction*[AspectRatiosNum]; - - for (int i = 0; i < 2; i++) - { - QActionGroup* group = grpScreenAspectTop; - QAction** actions = actScreenAspectTop; - - if (i == 1) - { - group = grpScreenAspectBot; - submenu->addSeparator(); - actions = actScreenAspectBot; - } - - for (int j = 0; j < AspectRatiosNum; j++) - { - auto ratio = aspectRatios[j]; - QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); - actions[j] = submenu->addAction(label); - actions[j]->setActionGroup(group); - actions[j]->setData(QVariant(ratio.id)); - actions[j]->setCheckable(true); - } - - connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); - } - } - - actScreenFiltering = menu->addAction("Screen filtering"); - actScreenFiltering->setCheckable(true); - connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); - - actShowOSD = menu->addAction("Show OSD"); - actShowOSD->setCheckable(true); - connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); - - menu->addSeparator(); - - actLimitFramerate = menu->addAction("Limit framerate"); - actLimitFramerate->setCheckable(true); - connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); - - actAudioSync = menu->addAction("Audio sync"); - actAudioSync->setCheckable(true); - connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); - } - setMenuBar(menubar); - - resize(Config::WindowWidth, Config::WindowHeight); - - if (Config::FirmwareUsername == "Arisotura") - actMPNewInstance->setText("Fart"); - -#ifdef Q_OS_MAC - QPoint screenCenter = screen()->availableGeometry().center(); - QRect frameGeo = frameGeometry(); - frameGeo.moveCenter(screenCenter); - move(frameGeo.topLeft()); -#endif - - if (oldMax) - showMaximized(); - else - show(); - - createScreenPanel(); - - actEjectCart->setEnabled(false); - actEjectGBACart->setEnabled(false); - - if (Config::ConsoleType == 1) - { - actInsertGBACart->setEnabled(false); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(false); - } - - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - actImportSavefile->setEnabled(false); - - actPause->setEnabled(false); - actReset->setEnabled(false); - actStop->setEnabled(false); - actFrameStep->setEnabled(false); - - actPowerManagement->setEnabled(false); - - actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); - - actEnableCheats->setChecked(Config::EnableCheats); - - actROMInfo->setEnabled(false); - actRAMInfo->setEnabled(false); - - actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); - - actScreenRotation[Config::ScreenRotation]->setChecked(true); - - for (int i = 0; i < 6; i++) - { - if (actScreenGap[i]->data().toInt() == Config::ScreenGap) - { - actScreenGap[i]->setChecked(true); - break; - } - } - - actScreenLayout[Config::ScreenLayout]->setChecked(true); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - actIntegerScaling->setChecked(Config::IntegerScaling); - - actScreenSwap->setChecked(Config::ScreenSwap); - - for (int i = 0; i < AspectRatiosNum; i++) - { - if (Config::ScreenAspectTop == aspectRatios[i].id) - actScreenAspectTop[i]->setChecked(true); - if (Config::ScreenAspectBot == aspectRatios[i].id) - actScreenAspectBot[i]->setChecked(true); - } - - actScreenFiltering->setChecked(Config::ScreenFilter); - actShowOSD->setChecked(Config::ShowOSD); - - actLimitFramerate->setChecked(Config::LimitFPS); - actAudioSync->setChecked(Config::AudioSync); - - if (inst > 0) - { - actEmuSettings->setEnabled(false); - actVideoSettings->setEnabled(false); - actMPSettings->setEnabled(false); - actWifiSettings->setEnabled(false); - actInterfaceSettings->setEnabled(false); - -#ifdef __APPLE__ - actPreferences->setEnabled(false); -#endif // __APPLE__ - } -} - -MainWindow::~MainWindow() -{ -} - -void MainWindow::closeEvent(QCloseEvent* event) -{ - if (hasOGL) - { - // we intentionally don't unpause here - emuThread->emuPause(); - emuThread->deinitContext(); - } - - QMainWindow::closeEvent(event); -} - -void MainWindow::createScreenPanel() -{ - hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != GPU::renderer3D_Software); - - if (hasOGL) - { - ScreenPanelGL* panelGL = new ScreenPanelGL(this); - panelGL->show(); - - panel = panelGL; - panelWidget = panelGL; - - panelGL->createContext(); - } - - if (!hasOGL) - { - ScreenPanelNative* panelNative = new ScreenPanelNative(this); - panel = panelNative; - panelWidget = panelNative; - panelWidget->show(); - } - setCentralWidget(panelWidget); - - actScreenFiltering->setEnabled(hasOGL); - - connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged())); - emit screenLayoutChange(); -} - -GL::Context* MainWindow::getOGLContext() -{ - if (!hasOGL) return nullptr; - - ScreenPanelGL* glpanel = static_cast(panel); - return glpanel->getContext(); -} - -void MainWindow::resizeEvent(QResizeEvent* event) -{ - int w = event->size().width(); - int h = event->size().height(); - - if (!isFullScreen()) - { - // this is ugly - // thing is, when maximizing the window, we first receive the resizeEvent - // with a new size matching the screen, then the changeEvent telling us that - // the maximized flag was updated - oldW = Config::WindowWidth; - oldH = Config::WindowHeight; - oldMax = isMaximized(); - - Config::WindowWidth = w; - Config::WindowHeight = h; - } -} - -void MainWindow::changeEvent(QEvent* event) -{ - if (isMaximized() && !oldMax) - { - Config::WindowWidth = oldW; - Config::WindowHeight = oldH; - } - - Config::WindowMaximized = isMaximized() ? 1:0; -} - -void MainWindow::keyPressEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) return; - - // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) NDS::debug(0); - - Input::KeyPress(event); -} - -void MainWindow::keyReleaseEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) return; - - Input::KeyRelease(event); -} - - -void MainWindow::dragEnterEvent(QDragEnterEvent* event) -{ - if (!event->mimeData()->hasUrls()) return; - - QList urls = event->mimeData()->urls(); - if (urls.count() > 1) return; // not handling more than one file at once - - QString filename = urls.at(0).toLocalFile(); - - if (FileIsSupportedFiletype(filename)) - event->acceptProposedAction(); -} - -void MainWindow::dropEvent(QDropEvent* event) -{ - if (!event->mimeData()->hasUrls()) return; - - QList urls = event->mimeData()->urls(); - if (urls.count() > 1) return; // not handling more than one file at once - - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - const QStringList file = splitArchivePath(urls.at(0).toLocalFile(), false); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - const QString filename = file.last(); - const bool romInsideArchive = file.size() > 1; - const auto matchMode = romInsideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; - const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchMode); - - bool isNdsRom = NdsRomByExtension(filename) || NdsRomByMimetype(mimetype); - bool isGbaRom = GbaRomByExtension(filename) || GbaRomByMimetype(mimetype); - isNdsRom |= ZstdNdsRomByExtension(filename); - isGbaRom |= ZstdGbaRomByExtension(filename); - - if (isNdsRom) - { - if (!ROMManager::LoadROM(file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the DS ROM."); - emuThread->emuUnpause(); - return; - } - - const QString barredFilename = file.join('|'); - recentFileList.removeAll(barredFilename); - recentFileList.prepend(barredFilename); - updateRecentFilesMenu(); - - NDS::Start(); - emuThread->emuRun(); - - updateCartInserted(false); - } - else if (isGbaRom) - { - if (!ROMManager::LoadGBAROM(file)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - - updateCartInserted(true); - } - else - { - QMessageBox::critical(this, "melonDS", "The file could not be recognized as a DS or GBA ROM."); - emuThread->emuUnpause(); - return; - } -} - -void MainWindow::focusInEvent(QFocusEvent* event) -{ - AudioInOut::AudioMute(mainWindow); -} - -void MainWindow::focusOutEvent(QFocusEvent* event) -{ - AudioInOut::AudioMute(mainWindow); -} - -void MainWindow::onAppStateChanged(Qt::ApplicationState state) -{ - if (state == Qt::ApplicationInactive) - { - if (Config::PauseLostFocus && emuThread->emuIsRunning()) - emuThread->emuPause(); - } - else if (state == Qt::ApplicationActive) - { - if (Config::PauseLostFocus && !pausedManually) - emuThread->emuUnpause(); - } -} - -bool MainWindow::verifySetup() -{ - QString res = ROMManager::VerifySetup(); - if (!res.isEmpty()) - { - QMessageBox::critical(this, "melonDS", res); - return false; - } - - return true; -} - -bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) -{ - if (!verifySetup()) - { - return false; - } - - bool gbaloaded = false; - if (!gbafile.isEmpty()) - { - if (!ROMManager::LoadGBAROM(gbafile)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); - return false; - } - - gbaloaded = true; - } - - bool ndsloaded = false; - if (!file.isEmpty()) - { - if (!ROMManager::LoadROM(file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - return false; - } - recentFileList.removeAll(file.join("|")); - recentFileList.prepend(file.join("|")); - updateRecentFilesMenu(); - ndsloaded = true; - } - - if (boot) - { - if (ndsloaded) - { - NDS::Start(); - emuThread->emuRun(); - } - else - { - onBootFirmware(); - } - } - - updateCartInserted(false); - - if (gbaloaded) - { - updateCartInserted(true); - } - - return true; -} - -QStringList MainWindow::splitArchivePath(const QString& filename, bool useMemberSyntax) -{ - if (filename.isEmpty()) return {}; - -#ifdef ARCHIVE_SUPPORT_ENABLED - if (useMemberSyntax) - { - const QStringList filenameParts = filename.split('|'); - if (filenameParts.size() > 2) - { - QMessageBox::warning(this, "melonDS", "This path contains too many '|'."); - return {}; - } - - if (filenameParts.size() == 2) - { - const QString archive = filenameParts.at(0); - if (!QFileInfo(archive).exists()) - { - QMessageBox::warning(this, "melonDS", "This archive does not exist."); - return {}; - } - - const QString subfile = filenameParts.at(1); - if (!Archive::ListArchive(archive).contains(subfile)) - { - QMessageBox::warning(this, "melonDS", "This archive does not contain the desired file."); - return {}; - } - - return filenameParts; - } - } -#endif - - if (!QFileInfo(filename).exists()) - { - QMessageBox::warning(this, "melonDS", "This ROM file does not exist."); - return {}; - } - -#ifdef ARCHIVE_SUPPORT_ENABLED - if (SupportedArchiveByExtension(filename) - || SupportedArchiveByMimetype(QMimeDatabase().mimeTypeForFile(filename))) - { - const QString subfile = pickFileFromArchive(filename); - if (subfile.isEmpty()) - return {}; - - return { filename, subfile }; - } -#endif - - return { filename }; -} - -QString MainWindow::pickFileFromArchive(QString archiveFileName) -{ - QVector archiveROMList = Archive::ListArchive(archiveFileName); - - if (archiveROMList.size() <= 1) - { - if (!archiveROMList.isEmpty() && archiveROMList.at(0) == "OK") - QMessageBox::warning(this, "melonDS", "This archive is empty."); - else - QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); - return QString(); - } - - archiveROMList.removeFirst(); - - const auto notSupportedRom = [&](const auto& filename){ - if (NdsRomByExtension(filename) || GbaRomByExtension(filename)) - return false; - const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension); - return !(NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype)); - }; - - archiveROMList.erase(std::remove_if(archiveROMList.begin(), archiveROMList.end(), notSupportedRom), - archiveROMList.end()); - - if (archiveROMList.isEmpty()) - { - QMessageBox::warning(this, "melonDS", "This archive does not contain any supported ROMs."); - return QString(); - } - - if (archiveROMList.size() == 1) - return archiveROMList.first(); - - bool ok; - const QString toLoad = QInputDialog::getItem( - this, "melonDS", - "This archive contains multiple files. Select which ROM you want to load.", - archiveROMList.toList(), 0, false, &ok - ); - - if (ok) return toLoad; - - // User clicked on cancel - - return QString(); -} - -QStringList MainWindow::pickROM(bool gba) -{ - const QString console = gba ? "GBA" : "DS"; - const QStringList& romexts = gba ? GbaRomExtensions : NdsRomExtensions; - - QString rawROMs = romexts.join(" *"); - QString extraFilters = ";;" + console + " ROMs (*" + rawROMs; - QString allROMs = rawROMs; - - QString zstdROMs = "*" + romexts.join(".zst *") + ".zst"; - extraFilters += ");;Zstandard-compressed " + console + " ROMs (" + zstdROMs + ")"; - allROMs += " " + zstdROMs; - -#ifdef ARCHIVE_SUPPORT_ENABLED - QString archives = "*" + ArchiveExtensions.join(" *"); - extraFilters += ";;Archives (" + archives + ")"; - allROMs += " " + archives; -#endif - extraFilters += ";;All files (*.*)"; - - const QString filename = QFileDialog::getOpenFileName( - this, "Open " + console + " ROM", - QString::fromStdString(Config::LastROMFolder), - "All supported files (*" + allROMs + ")" + extraFilters - ); - - if (filename.isEmpty()) return {}; - - Config::LastROMFolder = QFileInfo(filename).dir().path().toStdString(); - return splitArchivePath(filename, false); -} - -void MainWindow::updateCartInserted(bool gba) -{ - bool inserted; - if (gba) - { - inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); - actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); - actEjectGBACart->setEnabled(inserted); - } - else - { - inserted = ROMManager::CartInserted(); - actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); - actEjectCart->setEnabled(inserted); - actImportSavefile->setEnabled(inserted); - actSetupCheats->setEnabled(inserted); - actROMInfo->setEnabled(inserted); - actRAMInfo->setEnabled(inserted); - } -} - -void MainWindow::onOpenFile() -{ - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - QStringList file = pickROM(false); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadROM(file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - QString filename = file.join('|'); - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - NDS::Start(); - emuThread->emuRun(); - - updateCartInserted(false); -} - -void MainWindow::onClearRecentFiles() -{ - recentFileList.clear(); - for (int i = 0; i < 10; i++) - Config::RecentROMList[i] = ""; - updateRecentFilesMenu(); -} - -void MainWindow::updateRecentFilesMenu() -{ - recentMenu->clear(); - - for (int i = 0; i < recentFileList.size(); ++i) - { - if (i >= 10) break; - - QString item_full = recentFileList.at(i); - QString item_display = item_full; - int itemlen = item_full.length(); - const int maxlen = 100; - if (itemlen > maxlen) - { - int cut_start = 0; - while (item_full[cut_start] != '/' && item_full[cut_start] != '\\' && - cut_start < itemlen) - cut_start++; - - int cut_end = itemlen-1; - while (((item_full[cut_end] != '/' && item_full[cut_end] != '\\') || - (cut_start+4+(itemlen-cut_end) < maxlen)) && - cut_end > 0) - cut_end--; - - item_display.truncate(cut_start+1); - item_display += "..."; - item_display += QString(item_full).remove(0, cut_end); - } - - QAction *actRecentFile_i = recentMenu->addAction(QString("%1. %2").arg(i+1).arg(item_display)); - actRecentFile_i->setData(item_full); - connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); - - Config::RecentROMList[i] = recentFileList.at(i).toStdString(); - } - - while (recentFileList.size() > 10) - recentFileList.removeLast(); - - recentMenu->addSeparator(); - - QAction *actClearRecentList = recentMenu->addAction("Clear"); - connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles); - - if (recentFileList.empty()) - actClearRecentList->setEnabled(false); - - Config::Save(); -} - -void MainWindow::onClickRecentFile() -{ - QAction *act = (QAction *)sender(); - QString filename = act->data().toString(); - - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - const QStringList file = splitArchivePath(filename, true); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadROM(file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - NDS::Start(); - emuThread->emuRun(); - - updateCartInserted(false); -} - -void MainWindow::onBootFirmware() -{ - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadBIOS()) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); - emuThread->emuUnpause(); - return; - } - - NDS::Start(); - emuThread->emuRun(); -} - -void MainWindow::onInsertCart() -{ - emuThread->emuPause(); - - QStringList file = pickROM(false); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadROM(file, false)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - - updateCartInserted(false); -} - -void MainWindow::onEjectCart() -{ - emuThread->emuPause(); - - ROMManager::EjectCart(); - - emuThread->emuUnpause(); - - updateCartInserted(false); -} - -void MainWindow::onInsertGBACart() -{ - emuThread->emuPause(); - - QStringList file = pickROM(true); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadGBAROM(file)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - - updateCartInserted(true); -} - -void MainWindow::onInsertGBAAddon() -{ - QAction* act = (QAction*)sender(); - int type = act->data().toInt(); - - emuThread->emuPause(); - - ROMManager::LoadGBAAddon(type); - - emuThread->emuUnpause(); - - updateCartInserted(true); -} - -void MainWindow::onEjectGBACart() -{ - emuThread->emuPause(); - - ROMManager::EjectGBACart(); - - emuThread->emuUnpause(); - - updateCartInserted(true); -} - -void MainWindow::onSaveState() -{ - int slot = ((QAction*)sender())->data().toInt(); - - emuThread->emuPause(); - - std::string filename; - if (slot > 0) - { - filename = ROMManager::GetSavestateName(slot); - } - else - { - // TODO: specific 'last directory' for savestate files? - QString qfilename = QFileDialog::getSaveFileName(this, - "Save state", - QString::fromStdString(Config::LastROMFolder), - "melonDS savestates (*.mln);;Any file (*.*)"); - if (qfilename.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - filename = qfilename.toStdString(); - } - - if (ROMManager::SaveState(filename)) - { - char msg[64]; - if (slot > 0) sprintf(msg, "State saved to slot %d", slot); - else sprintf(msg, "State saved to file"); - OSD::AddMessage(0, msg); - - actLoadState[slot]->setEnabled(true); - } - else - { - OSD::AddMessage(0xFFA0A0, "State save failed"); - } - - emuThread->emuUnpause(); -} - -void MainWindow::onLoadState() -{ - int slot = ((QAction*)sender())->data().toInt(); - - emuThread->emuPause(); - - std::string filename; - if (slot > 0) - { - filename = ROMManager::GetSavestateName(slot); - } - else - { - // TODO: specific 'last directory' for savestate files? - QString qfilename = QFileDialog::getOpenFileName(this, - "Load state", - QString::fromStdString(Config::LastROMFolder), - "melonDS savestates (*.ml*);;Any file (*.*)"); - if (qfilename.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - filename = qfilename.toStdString(); - } - - if (!Platform::FileExists(filename)) - { - char msg[64]; - if (slot > 0) sprintf(msg, "State slot %d is empty", slot); - else sprintf(msg, "State file does not exist"); - OSD::AddMessage(0xFFA0A0, msg); - - emuThread->emuUnpause(); - return; - } - - if (ROMManager::LoadState(filename)) - { - char msg[64]; - if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); - else sprintf(msg, "State loaded from file"); - OSD::AddMessage(0, msg); - - actUndoStateLoad->setEnabled(true); - } - else - { - OSD::AddMessage(0xFFA0A0, "State load failed"); - } - - emuThread->emuUnpause(); -} - -void MainWindow::onUndoStateLoad() -{ - emuThread->emuPause(); - ROMManager::UndoStateLoad(); - emuThread->emuUnpause(); - - OSD::AddMessage(0, "State load undone"); -} - -void MainWindow::onImportSavefile() -{ - emuThread->emuPause(); - QString path = QFileDialog::getOpenFileName(this, - "Select savefile", - QString::fromStdString(Config::LastROMFolder), - "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); - - if (path.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - Platform::FileHandle* f = Platform::OpenFile(path.toStdString(), Platform::FileMode::Read); - if (!f) - { - QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); - emuThread->emuUnpause(); - return; - } - - if (RunningSomething) - { - if (QMessageBox::warning(this, - "melonDS", - "The emulation will be reset and the current savefile overwritten.", - QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) - { - emuThread->emuUnpause(); - return; - } - - ROMManager::Reset(); - } - - u32 len = FileLength(f); - - u8* data = new u8[len]; - Platform::FileRewind(f); - Platform::FileRead(data, len, 1, f); - - NDS::LoadSave(data, len); - delete[] data; - - CloseFile(f); - emuThread->emuUnpause(); -} - -void MainWindow::onQuit() -{ -#ifndef _WIN32 - signalSn->setEnabled(false); -#endif - QApplication::quit(); -} - - -void MainWindow::onPause(bool checked) -{ - if (!RunningSomething) return; - - if (checked) - { - emuThread->emuPause(); - OSD::AddMessage(0, "Paused"); - pausedManually = true; - } - else - { - emuThread->emuUnpause(); - OSD::AddMessage(0, "Resumed"); - pausedManually = false; - } -} - -void MainWindow::onReset() -{ - if (!RunningSomething) return; - - emuThread->emuPause(); - - actUndoStateLoad->setEnabled(false); - - ROMManager::Reset(); - - OSD::AddMessage(0, "Reset"); - emuThread->emuRun(); -} - -void MainWindow::onStop() -{ - if (!RunningSomething) return; - - emuThread->emuPause(); - NDS::Stop(); -} - -void MainWindow::onFrameStep() -{ - if (!RunningSomething) return; - - emuThread->emuFrameStep(); -} - -void MainWindow::onEnableCheats(bool checked) -{ - Config::EnableCheats = checked?1:0; - ROMManager::EnableCheats(Config::EnableCheats != 0); -} - -void MainWindow::onSetupCheats() -{ - emuThread->emuPause(); - - CheatsDialog* dlg = CheatsDialog::openDlg(this); - connect(dlg, &CheatsDialog::finished, this, &MainWindow::onCheatsDialogFinished); -} - -void MainWindow::onCheatsDialogFinished(int res) -{ - emuThread->emuUnpause(); -} - -void MainWindow::onROMInfo() -{ - ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this); -} - -void MainWindow::onRAMInfo() -{ - RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this); -} - -void MainWindow::onOpenTitleManager() -{ - TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this); -} - -void MainWindow::onMPNewInstance() -{ - //QProcess::startDetached(QApplication::applicationFilePath()); - QProcess newinst; - newinst.setProgram(QApplication::applicationFilePath()); - newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1)); - -#ifdef __WIN32__ - newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args) - { - args->flags |= CREATE_NEW_CONSOLE; - }); -#endif - - newinst.startDetached(); -} - -void MainWindow::onOpenEmuSettings() -{ - emuThread->emuPause(); - - EmuSettingsDialog* dlg = EmuSettingsDialog::openDlg(this); - connect(dlg, &EmuSettingsDialog::finished, this, &MainWindow::onEmuSettingsDialogFinished); -} - -void MainWindow::onEmuSettingsDialogFinished(int res) -{ - emuThread->emuUnpause(); - - if (Config::ConsoleType == 1) - { - actInsertGBACart->setEnabled(false); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(false); - actEjectGBACart->setEnabled(false); - } - else - { - actInsertGBACart->setEnabled(true); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(true); - actEjectGBACart->setEnabled(ROMManager::GBACartInserted()); - } - - if (EmuSettingsDialog::needsReset) - onReset(); - - actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); - - if (!RunningSomething) - actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); -} - -void MainWindow::onOpenPowerManagement() -{ - PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this); -} - -void MainWindow::onOpenInputConfig() -{ - emuThread->emuPause(); - - InputConfigDialog* dlg = InputConfigDialog::openDlg(this); - connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); -} - -void MainWindow::onInputConfigFinished(int res) -{ - emuThread->emuUnpause(); -} - -void MainWindow::onOpenVideoSettings() -{ - VideoSettingsDialog* dlg = VideoSettingsDialog::openDlg(this); - connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings); -} - -void MainWindow::onOpenCameraSettings() -{ - emuThread->emuPause(); - - camStarted[0] = camManager[0]->isStarted(); - camStarted[1] = camManager[1]->isStarted(); - if (camStarted[0]) camManager[0]->stop(); - if (camStarted[1]) camManager[1]->stop(); - - CameraSettingsDialog* dlg = CameraSettingsDialog::openDlg(this); - connect(dlg, &CameraSettingsDialog::finished, this, &MainWindow::onCameraSettingsFinished); -} - -void MainWindow::onCameraSettingsFinished(int res) -{ - if (camStarted[0]) camManager[0]->start(); - if (camStarted[1]) camManager[1]->start(); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenAudioSettings() -{ - AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive()); - connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel); - connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset); - connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); - connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); -} - -void MainWindow::onOpenFirmwareSettings() -{ - emuThread->emuPause(); - - FirmwareSettingsDialog* dlg = FirmwareSettingsDialog::openDlg(this); - connect(dlg, &FirmwareSettingsDialog::finished, this, &MainWindow::onFirmwareSettingsFinished); -} - -void MainWindow::onFirmwareSettingsFinished(int res) -{ - if (FirmwareSettingsDialog::needsReset) - onReset(); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenPathSettings() -{ - emuThread->emuPause(); - - PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this); - connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished); -} - -void MainWindow::onPathSettingsFinished(int res) -{ - if (PathSettingsDialog::needsReset) - onReset(); - - emuThread->emuUnpause(); -} - -void MainWindow::onUpdateAudioSettings() -{ - SPU::SetInterpolation(Config::AudioInterp); - - if (Config::AudioBitDepth == 0) - SPU::SetDegrade10Bit(NDS::ConsoleType == 0); - else - SPU::SetDegrade10Bit(Config::AudioBitDepth == 1); -} - -void MainWindow::onAudioSettingsFinished(int res) -{ - AudioInOut::UpdateSettings(); -} - -void MainWindow::onOpenMPSettings() -{ - emuThread->emuPause(); - - MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this); - connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished); -} - -void MainWindow::onMPSettingsFinished(int res) -{ - AudioInOut::AudioMute(mainWindow); - LocalMP::SetRecvTimeout(Config::MPRecvTimeout); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenWifiSettings() -{ - emuThread->emuPause(); - - WifiSettingsDialog* dlg = WifiSettingsDialog::openDlg(this); - connect(dlg, &WifiSettingsDialog::finished, this, &MainWindow::onWifiSettingsFinished); -} - -void MainWindow::onWifiSettingsFinished(int res) -{ - Platform::LAN_DeInit(); - Platform::LAN_Init(); - - if (WifiSettingsDialog::needsReset) - onReset(); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenInterfaceSettings() -{ - emuThread->emuPause(); - InterfaceSettingsDialog* dlg = InterfaceSettingsDialog::openDlg(this); - connect(dlg, &InterfaceSettingsDialog::finished, this, &MainWindow::onInterfaceSettingsFinished); - connect(dlg, &InterfaceSettingsDialog::updateMouseTimer, this, &MainWindow::onUpdateMouseTimer); -} - -void MainWindow::onUpdateMouseTimer() -{ - panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000); -} - -void MainWindow::onInterfaceSettingsFinished(int res) -{ - emuThread->emuUnpause(); -} - -void MainWindow::onChangeSavestateSRAMReloc(bool checked) -{ - Config::SavestateRelocSRAM = checked?1:0; -} - -void MainWindow::onChangeScreenSize() -{ - int factor = ((QAction*)sender())->data().toInt(); - QSize diff = size() - panelWidget->size(); - resize(panel->screenGetMinSize(factor) + diff); -} - -void MainWindow::onChangeScreenRotation(QAction* act) -{ - int rot = act->data().toInt(); - Config::ScreenRotation = rot; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenGap(QAction* act) -{ - int gap = act->data().toInt(); - Config::ScreenGap = gap; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenLayout(QAction* act) -{ - int layout = act->data().toInt(); - Config::ScreenLayout = layout; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenSwap(bool checked) -{ - Config::ScreenSwap = checked?1:0; - - // Swap between top and bottom screen when displaying one screen. - if (Config::ScreenSizing == Frontend::screenSizing_TopOnly) - { - // Bottom Screen. - Config::ScreenSizing = Frontend::screenSizing_BotOnly; - actScreenSizing[Frontend::screenSizing_TopOnly]->setChecked(false); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - } - else if (Config::ScreenSizing == Frontend::screenSizing_BotOnly) - { - // Top Screen. - Config::ScreenSizing = Frontend::screenSizing_TopOnly; - actScreenSizing[Frontend::screenSizing_BotOnly]->setChecked(false); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - } - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenSizing(QAction* act) -{ - int sizing = act->data().toInt(); - Config::ScreenSizing = sizing; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenAspect(QAction* act) -{ - int aspect = act->data().toInt(); - QActionGroup* group = act->actionGroup(); - - if (group == grpScreenAspectTop) - { - Config::ScreenAspectTop = aspect; - } - else - { - Config::ScreenAspectBot = aspect; - } - - emit screenLayoutChange(); -} - -void MainWindow::onChangeIntegerScaling(bool checked) -{ - Config::IntegerScaling = checked?1:0; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenFiltering(bool checked) -{ - Config::ScreenFilter = checked?1:0; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeShowOSD(bool checked) -{ - Config::ShowOSD = checked?1:0; -} -void MainWindow::onChangeLimitFramerate(bool checked) -{ - Config::LimitFPS = checked?1:0; -} - -void MainWindow::onChangeAudioSync(bool checked) -{ - Config::AudioSync = checked?1:0; -} - - -void MainWindow::onTitleUpdate(QString title) -{ - setWindowTitle(title); -} - -void ToggleFullscreen(MainWindow* mainWindow) -{ - if (!mainWindow->isFullScreen()) - { - mainWindow->showFullScreen(); - mainWindow->menuBar()->setFixedHeight(0); // Don't use hide() as menubar actions stop working - } - else - { - mainWindow->showNormal(); - int menuBarHeight = mainWindow->menuBar()->sizeHint().height(); - mainWindow->menuBar()->setFixedHeight(menuBarHeight); - } -} - -void MainWindow::onFullscreenToggled() -{ - ToggleFullscreen(this); -} - -void MainWindow::onScreenEmphasisToggled() -{ - int currentSizing = Config::ScreenSizing; - if (currentSizing == Frontend::screenSizing_EmphTop) - { - Config::ScreenSizing = Frontend::screenSizing_EmphBot; - } - else if (currentSizing == Frontend::screenSizing_EmphBot) - { - Config::ScreenSizing = Frontend::screenSizing_EmphTop; - } - - emit screenLayoutChange(); -} - -void MainWindow::onEmuStart() -{ - for (int i = 1; i < 9; i++) - { - actSaveState[i]->setEnabled(true); - actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); - } - actSaveState[0]->setEnabled(true); - actLoadState[0]->setEnabled(true); - actUndoStateLoad->setEnabled(false); - - actPause->setEnabled(true); - actPause->setChecked(false); - actReset->setEnabled(true); - actStop->setEnabled(true); - actFrameStep->setEnabled(true); - - actPowerManagement->setEnabled(true); - - actTitleManager->setEnabled(false); -} - -void MainWindow::onEmuStop() -{ - emuThread->emuPause(); - - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - - actPause->setEnabled(false); - actReset->setEnabled(false); - actStop->setEnabled(false); - actFrameStep->setEnabled(false); - - actPowerManagement->setEnabled(false); - - actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); -} - -void MainWindow::onUpdateVideoSettings(bool glchange) -{ - if (glchange) - { - emuThread->emuPause(); - if (hasOGL) emuThread->deinitContext(); - - delete panel; - createScreenPanel(); - connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint())); - } - - videoSettingsDirty = true; - - if (glchange) - { - if (hasOGL) emuThread->initContext(); - emuThread->emuUnpause(); - } -} void emuStop() @@ -3206,8 +264,11 @@ void emuStop() MelonApplication::MelonApplication(int& argc, char** argv) : QApplication(argc, argv) { -#ifndef __APPLE__ +#if !defined(Q_OS_APPLE) setWindowIcon(QIcon(":/melon-icon")); + #if defined(Q_OS_UNIX) + setDesktopFileName(QString("net.kuribo64.melonDS")); + #endif #endif } @@ -3232,6 +293,11 @@ int main(int argc, char** argv) qputenv("QT_SCALE_FACTOR", "1"); +#if QT_VERSION_MAJOR == 6 && defined(__WIN32__) + // Allow using the system dark theme palette on Windows + qputenv("QT_QPA_PLATFORM", "windows:darkmode=2"); +#endif + printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); @@ -3239,10 +305,10 @@ int main(int argc, char** argv) if (argc != 0 && (!strcasecmp(argv[0], "derpDS") || !strcasecmp(argv[0], "./derpDS"))) printf("did you just call me a derp???\n"); - Platform::Init(argc, argv); - MelonApplication melon(argc, argv); + Platform::Init(argc, argv); + CLI::CommandLineOptions* options = CLI::ManageArgs(melon); // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl @@ -3271,14 +337,18 @@ int main(int argc, char** argv) SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_EnableScreenSaver(); SDL_DisableScreenSaver(); - Config::Load(); + if (!Config::Load()) QMessageBox::critical(NULL, "melonDS", "Unable to write to config.\nPlease check the write permissions of the folder you placed melonDS in."); #define SANITIZE(var, min, max) { var = std::clamp(var, min, max); } SANITIZE(Config::ConsoleType, 0, 1); - SANITIZE(Config::_3DRenderer, (int)GPU::renderer3D_Software, (int)GPU::renderer3D_Max); +#ifdef OGLRENDERER_ENABLED + SANITIZE(Config::_3DRenderer, 0, 1); // 0 is the software renderer, 1 is the OpenGL renderer +#else + SANITIZE(Config::_3DRenderer, 0, 0); +#endif SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::GL_ScaleFactor, 1, 16); - SANITIZE(Config::AudioInterp, 0, 3); + SANITIZE(Config::AudioInterp, 0, 4); SANITIZE(Config::AudioVolume, 0, 256); SANITIZE(Config::MicInputType, 0, (int)micInputType_MAX); SANITIZE(Config::ScreenRotation, 0, (int)Frontend::screenRot_MAX); @@ -3289,7 +359,6 @@ int main(int argc, char** argv) SANITIZE(Config::ScreenAspectBot, 0, AspectRatiosNum); #undef SANITIZE - AudioInOut::Init(); camStarted[0] = false; camStarted[1] = false; camManager[0] = new CameraManager(0, 640, 480, true); @@ -3297,7 +366,12 @@ int main(int argc, char** argv) camManager[0]->setXFlip(Config::Camera[0].XFlip); camManager[1]->setXFlip(Config::Camera[1].XFlip); - ROMManager::EnableCheats(Config::EnableCheats != 0); + systemThemeName = new QString(QApplication::style()->objectName()); + + if (!Config::UITheme.empty()) + { + QApplication::setStyle(QString::fromStdString(Config::UITheme)); + } Input::JoystickID = Config::JoystickID; Input::OpenJoystick(); @@ -3310,6 +384,8 @@ int main(int argc, char** argv) emuThread->start(); emuThread->emuPause(); + AudioInOut::Init(emuThread); + ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); AudioInOut::AudioMute(mainWindow); QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged); @@ -3337,6 +413,8 @@ int main(int argc, char** argv) int ret = melon.exec(); + delete options; + emuThread->emuStop(); emuThread->wait(); delete emuThread; diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index aa94a625..5751f229 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,221 +22,18 @@ #include "glad/glad.h" #include -#include #include #include #include #include #include #include -#include #include #include -#include - -#include - +#include "Window.h" +#include "EmuThread.h" #include "FrontendUtil.h" -#include "duckstation/gl/context.h" - -class EmuThread : public QThread -{ - Q_OBJECT - void run() override; - -public: - explicit EmuThread(QObject* parent = nullptr); - - void changeWindowTitle(char* title); - - // to be called from the UI thread - void emuRun(); - void emuPause(); - void emuUnpause(); - void emuStop(); - void emuFrameStep(); - - bool emuIsRunning(); - bool emuIsActive(); - - void initContext(); - void deinitContext(); - - int FrontBuffer = 0; - QMutex FrontBufferLock; - - void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); - -signals: - void windowUpdate(); - void windowTitleChange(QString title); - - void windowEmuStart(); - void windowEmuStop(); - void windowEmuPause(); - void windowEmuReset(); - void windowEmuFrameStep(); - - void windowLimitFPSChange(); - - void screenLayoutChange(); - - void windowFullscreenToggle(); - - void swapScreensToggle(); - void screenEmphasisToggle(); - - void syncVolumeLevel(); - -private: - void drawScreenGL(); - void initOpenGL(); - void deinitOpenGL(); - - enum EmuStatusKind - { - emuStatus_Exit, - emuStatus_Running, - emuStatus_Paused, - emuStatus_FrameStep, - }; - std::atomic EmuStatus; - - EmuStatusKind PrevEmuStatus; - EmuStatusKind EmuRunning; - - constexpr static int EmuPauseStackRunning = 0; - constexpr static int EmuPauseStackPauseThreshold = 1; - int EmuPauseStack; - - enum ContextRequestKind - { - contextRequest_None = 0, - contextRequest_InitGL, - contextRequest_DeInitGL - }; - std::atomic ContextRequest = contextRequest_None; - - GL::Context* oglContext = nullptr; - GLuint screenVertexBuffer, screenVertexArray; - GLuint screenTexture; - GLuint screenShaderProgram; - GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; - - QMutex screenSettingsLock; - WindowInfo windowInfo; - float screenMatrix[Frontend::MaxScreenTransforms][6]; - int screenKind[Frontend::MaxScreenTransforms]; - int numScreens; - bool filter; - - int lastScreenWidth = -1, lastScreenHeight = -1; -}; - - -class ScreenHandler -{ - Q_GADGET - -public: - ScreenHandler(QWidget* widget); - virtual ~ScreenHandler(); - QTimer* setupMouseTimer(); - void updateMouseTimer(); - QTimer* mouseTimer; - QSize screenGetMinSize(int factor); - -protected: - void screenSetupLayout(int w, int h); - - void screenOnMousePress(QMouseEvent* event); - void screenOnMouseRelease(QMouseEvent* event); - void screenOnMouseMove(QMouseEvent* event); - - void screenHandleTablet(QTabletEvent* event); - void screenHandleTouch(QTouchEvent* event); - - float screenMatrix[Frontend::MaxScreenTransforms][6]; - int screenKind[Frontend::MaxScreenTransforms]; - int numScreens; - - bool touching = false; - - void showCursor(); -}; - - -class ScreenPanelNative : public QWidget, public ScreenHandler -{ - Q_OBJECT - -public: - explicit ScreenPanelNative(QWidget* parent); - virtual ~ScreenPanelNative(); - -protected: - void paintEvent(QPaintEvent* event) override; - - void resizeEvent(QResizeEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - - void tabletEvent(QTabletEvent* event) override; - bool event(QEvent* event) override; -private slots: - void onScreenLayoutChanged(); - -private: - void setupScreenLayout(); - - QImage screen[2]; - QTransform screenTrans[Frontend::MaxScreenTransforms]; -}; - - -class ScreenPanelGL : public QWidget, public ScreenHandler -{ - Q_OBJECT - -public: - explicit ScreenPanelGL(QWidget* parent); - virtual ~ScreenPanelGL(); - - std::optional getWindowInfo(); - - bool createContext(); - - GL::Context* getContext() { return glContext.get(); } - - void transferLayout(EmuThread* thread); -protected: - - qreal devicePixelRatioFromScreen() const; - int scaledWindowWidth() const; - int scaledWindowHeight() const; - - QPaintEngine* paintEngine() const override; - - void resizeEvent(QResizeEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - - void tabletEvent(QTabletEvent* event) override; - bool event(QEvent* event) override; - -private slots: - void onScreenLayoutChanged(); - -private: - void setupScreenLayout(); - - std::unique_ptr glContext; -}; class MelonApplication : public QApplication { @@ -247,197 +44,6 @@ public: bool event(QEvent* event) override; }; -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget* parent = nullptr); - ~MainWindow(); - - bool hasOGL; - GL::Context* getOGLContext(); - - bool preloadROMs(QStringList file, QStringList gbafile, bool boot); - QStringList splitArchivePath(const QString& filename, bool useMemberSyntax); - - void onAppStateChanged(Qt::ApplicationState state); - -protected: - void resizeEvent(QResizeEvent* event) override; - void changeEvent(QEvent* event) override; - - void keyPressEvent(QKeyEvent* event) override; - void keyReleaseEvent(QKeyEvent* event) override; - - void dragEnterEvent(QDragEnterEvent* event) override; - void dropEvent(QDropEvent* event) override; - - void focusInEvent(QFocusEvent* event) override; - void focusOutEvent(QFocusEvent* event) override; - -signals: - void screenLayoutChange(); - -private slots: - void onOpenFile(); - void onClickRecentFile(); - void onClearRecentFiles(); - void onBootFirmware(); - void onInsertCart(); - void onEjectCart(); - void onInsertGBACart(); - void onInsertGBAAddon(); - void onEjectGBACart(); - void onSaveState(); - void onLoadState(); - void onUndoStateLoad(); - void onImportSavefile(); - void onQuit(); - - void onPause(bool checked); - void onReset(); - void onStop(); - void onFrameStep(); - void onEnableCheats(bool checked); - void onSetupCheats(); - void onCheatsDialogFinished(int res); - void onROMInfo(); - void onRAMInfo(); - void onOpenTitleManager(); - void onMPNewInstance(); - - void onOpenEmuSettings(); - void onEmuSettingsDialogFinished(int res); - void onOpenPowerManagement(); - void onOpenInputConfig(); - void onInputConfigFinished(int res); - void onOpenVideoSettings(); - void onOpenCameraSettings(); - void onCameraSettingsFinished(int res); - void onOpenAudioSettings(); - void onUpdateAudioSettings(); - void onAudioSettingsFinished(int res); - void onOpenMPSettings(); - void onMPSettingsFinished(int res); - void onOpenWifiSettings(); - void onWifiSettingsFinished(int res); - void onOpenFirmwareSettings(); - void onFirmwareSettingsFinished(int res); - void onOpenPathSettings(); - void onPathSettingsFinished(int res); - void onOpenInterfaceSettings(); - void onInterfaceSettingsFinished(int res); - void onUpdateMouseTimer(); - void onChangeSavestateSRAMReloc(bool checked); - void onChangeScreenSize(); - void onChangeScreenRotation(QAction* act); - void onChangeScreenGap(QAction* act); - void onChangeScreenLayout(QAction* act); - void onChangeScreenSwap(bool checked); - void onChangeScreenSizing(QAction* act); - void onChangeScreenAspect(QAction* act); - void onChangeIntegerScaling(bool checked); - void onChangeScreenFiltering(bool checked); - void onChangeShowOSD(bool checked); - void onChangeLimitFramerate(bool checked); - void onChangeAudioSync(bool checked); - - void onTitleUpdate(QString title); - - void onEmuStart(); - void onEmuStop(); - - void onUpdateVideoSettings(bool glchange); - - void onFullscreenToggled(); - void onScreenEmphasisToggled(); - -private: - virtual void closeEvent(QCloseEvent* event) override; - - QStringList currentROM; - QStringList currentGBAROM; - QList recentFileList; - QMenu *recentMenu; - void updateRecentFilesMenu(); - - bool verifySetup(); - QString pickFileFromArchive(QString archiveFileName); - QStringList pickROM(bool gba); - void updateCartInserted(bool gba); - - void createScreenPanel(); - - bool pausedManually = false; - - int oldW, oldH; - bool oldMax; - -public: - ScreenHandler* panel; - QWidget* panelWidget; - - QAction* actOpenROM; - QAction* actBootFirmware; - QAction* actCurrentCart; - QAction* actInsertCart; - QAction* actEjectCart; - QAction* actCurrentGBACart; - QAction* actInsertGBACart; - QAction* actInsertGBAAddon[1]; - QAction* actEjectGBACart; - QAction* actImportSavefile; - QAction* actSaveState[9]; - QAction* actLoadState[9]; - QAction* actUndoStateLoad; - QAction* actQuit; - - QAction* actPause; - QAction* actReset; - QAction* actStop; - QAction* actFrameStep; - QAction* actEnableCheats; - QAction* actSetupCheats; - QAction* actROMInfo; - QAction* actRAMInfo; - QAction* actTitleManager; - QAction* actMPNewInstance; - - QAction* actEmuSettings; -#ifdef __APPLE__ - QAction* actPreferences; -#endif - QAction* actPowerManagement; - QAction* actInputConfig; - QAction* actVideoSettings; - QAction* actCameraSettings; - QAction* actAudioSettings; - QAction* actMPSettings; - QAction* actWifiSettings; - QAction* actFirmwareSettings; - QAction* actPathSettings; - QAction* actInterfaceSettings; - QAction* actSavestateSRAMReloc; - QAction* actScreenSize[4]; - QActionGroup* grpScreenRotation; - QAction* actScreenRotation[Frontend::screenRot_MAX]; - QActionGroup* grpScreenGap; - QAction* actScreenGap[6]; - QActionGroup* grpScreenLayout; - QAction* actScreenLayout[Frontend::screenLayout_MAX]; - QAction* actScreenSwap; - QActionGroup* grpScreenSizing; - QAction* actScreenSizing[Frontend::screenSizing_MAX]; - QAction* actIntegerScaling; - QActionGroup* grpScreenAspectTop; - QAction** actScreenAspectTop; - QActionGroup* grpScreenAspectBot; - QAction** actScreenAspectBot; - QAction* actScreenFiltering; - QAction* actShowOSD; - QAction* actLimitFramerate; - QAction* actAudioSync; -}; +extern QString* systemThemeName; #endif // MAIN_H diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h index 37b964d5..fd9de038 100644 --- a/src/frontend/qt_sdl/main_shaders.h +++ b/src/frontend/qt_sdl/main_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/melonDLDI.h b/src/melonDLDI.h index 261f798b..908a2f60 100644 --- a/src/melonDLDI.h +++ b/src/melonDLDI.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,6 +21,8 @@ #include "types.h" +namespace melonDS +{ const u8 melonDLDI[] = { 0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x09, 0x00, 0x00, @@ -54,4 +56,5 @@ const u8 melonDLDI[] = 0x1E, 0xFF, 0x2F, 0xE1 }; +} #endif // MELONDLDI_H diff --git a/src/sha1/sha1.c b/src/sha1/sha1.c index c0052b70..c34ace30 100644 --- a/src/sha1/sha1.c +++ b/src/sha1/sha1.c @@ -27,6 +27,9 @@ A million repetitions of "a" #if defined(__sun) #include "solarisfixes.h" #endif +#if defined(__HAIKU__) +#include +#endif #include "sha1.h" #ifndef BYTE_ORDER diff --git a/src/teakra/src/CMakeLists.txt b/src/teakra/src/CMakeLists.txt index b96c500b..30683374 100644 --- a/src/teakra/src/CMakeLists.txt +++ b/src/teakra/src/CMakeLists.txt @@ -32,11 +32,16 @@ add_library(teakra register.h shared_memory.h teakra.cpp - test.h - test_generator.cpp - test_generator.h ) +if (TEAKRA_BUILD_UNIT_TESTS) + target_sources(teakra PUBLIC + test.h + test_generator.cpp + test_generator.h + ) +endif() + create_target_directory_groups(teakra) target_link_libraries(teakra PRIVATE Threads::Threads) diff --git a/src/types.h b/src/types.h index 47c1b9e4..86a10aa7 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -20,7 +20,10 @@ #define TYPES_H #include +#include +namespace melonDS +{ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; @@ -30,4 +33,7 @@ typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; +template +using array2d = std::array, A>; +} #endif // TYPES_H diff --git a/src/version.h b/src/version.h.in similarity index 72% rename from src/version.h rename to src/version.h.in index f3878206..a3db45b3 100644 --- a/src/version.h +++ b/src/version.h.in @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -19,7 +19,11 @@ #ifndef VERSION_H #define VERSION_H -#define MELONDS_URL "https://melonds.kuribo64.net/" +#define MELONDS_URL "${melonDS_HOMEPAGE_URL}" + +#define MELONDS_VERSION_BASE "${melonDS_VERSION}" +#define MELONDS_VERSION_SUFFIX "${MELONDS_VERSION_SUFFIX}" +#define MELONDS_VERSION MELONDS_VERSION_BASE MELONDS_VERSION_SUFFIX #endif // VERSION_H diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..3e23ff1b --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,27 @@ +{ + "dependencies": [ + "sdl2", + { + "name": "libarchive", + "default-features": false, + "features": ["bzip2", "crypto", "lz4", "zstd"] + }, + "libslirp", + "zstd", + { + "name": "qtbase", + "default-features": false, + "features": ["gui", "png", "thread", "widgets", "opengl", "zstd"] + }, + { + "name": "qtbase", + "host": true, + "default-features": false + }, + { + "name": "qtmultimedia", + "default-features": false + }, + "qtsvg" + ] +}