diff --git a/.github/actions/notify_success/action.yml b/.github/actions/notify_success/action.yml new file mode 100644 index 000000000..9b50d87bf --- /dev/null +++ b/.github/actions/notify_success/action.yml @@ -0,0 +1,19 @@ +name: Notify success +description: Sends a notification that a workflow has finished +inputs: + DESTINATION_URL: + description: 'Webhook notification URL' + type: string + WORKFLOW_NAME: + description: 'Workflow name' + required: true + type: string + +runs: + using: "composite" + steps: + - name: Notify failure + if: ${{ inputs.DESTINATION_URL != '' }} + shell: sh + run: | + curl ${{ inputs.DESTINATION_URL }}/${{ inputs.WORKFLOW_NAME }}/${{ github.sha }} \ No newline at end of file diff --git a/.github/actions/upload_build/action.yml b/.github/actions/upload_build/action.yml index 36fc2fcbe..84f896586 100644 --- a/.github/actions/upload_build/action.yml +++ b/.github/actions/upload_build/action.yml @@ -13,7 +13,7 @@ inputs: runs: using: "composite" steps: - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ inputs.DEST_NAME }} path: ${{ inputs.SOURCE_FILE }} \ No newline at end of file diff --git a/.github/workflows/build_3ds.yml b/.github/workflows/build_3ds.yml index dbb37d682..865be837e 100644 --- a/.github/workflows/build_3ds.yml +++ b/.github/workflows/build_3ds.yml @@ -26,6 +26,12 @@ jobs: WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-3ds.cia' + DEST_NAME: 'ClassiCube-3ds.cia' + - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: @@ -36,4 +42,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-3ds.elf' - DEST_NAME: 'ClassiCube-3ds.elf' \ No newline at end of file + DEST_NAME: 'ClassiCube-3ds.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: '3ds' \ No newline at end of file diff --git a/.github/workflows/build_dreamcast.yml b/.github/workflows/build_dreamcast.yml index 6825bb317..8234a559a 100644 --- a/.github/workflows/build_dreamcast.yml +++ b/.github/workflows/build_dreamcast.yml @@ -38,4 +38,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-dc.elf' - DEST_NAME: 'ClassiCube-dc.elf' \ No newline at end of file + DEST_NAME: 'ClassiCube-dc.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'dreamcast' \ No newline at end of file diff --git a/.github/workflows/build_freebsd.yml b/.github/workflows/build_freebsd.yml index 6afba71a7..059587f7c 100644 --- a/.github/workflows/build_freebsd.yml +++ b/.github/workflows/build_freebsd.yml @@ -35,8 +35,8 @@ jobs: PLAT32_FLAGS: "-fno-pie -fvisibility=hidden -fcf-protection=none -rdynamic -DCC_BUILD_ICON -I freebsd32/include -L freebsd32/lib" PLAT64_FLAGS: "-fno-pie -fvisibility=hidden -fcf-protection=none -rdynamic -DCC_BUILD_ICON -I freebsd64/include -L freebsd64/lib" run: | + apk add curl LATEST_FLAG=-DCC_COMMIT_SHA=\"${GITHUB_SHA::9}\" - echo $LATEST_FLAG cd src i386-freebsd11-clang *.c ${{ env.COMMON_FLAGS }} ${{ env.PLAT32_FLAGS }} $LATEST_FLAG -o cc-fbsd32-gl1 -lm -lpthread -lX11 -lXi -lGL -lexecinfo @@ -65,4 +65,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'src/cc-fbsd64-gl1' - DEST_NAME: 'ClassiCube-FreeBSD-64' \ No newline at end of file + DEST_NAME: 'ClassiCube-FreeBSD-64' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'freebsd' \ No newline at end of file diff --git a/.github/workflows/build_haiku.yml b/.github/workflows/build_haiku.yml index 25ccd6890..de6789c11 100644 --- a/.github/workflows/build_haiku.yml +++ b/.github/workflows/build_haiku.yml @@ -33,4 +33,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'src/ClassiCube-haiku' - DEST_NAME: 'ClassiCube-haiku' \ No newline at end of file + DEST_NAME: 'ClassiCube-haiku' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'haiku' \ No newline at end of file diff --git a/.github/workflows/build_ios.yml b/.github/workflows/build_ios.yml new file mode 100644 index 000000000..45417daab --- /dev/null +++ b/.github/workflows/build_ios.yml @@ -0,0 +1,41 @@ +name: Build latest (iOS) +on: [push] + +concurrency: + group: ${{ github.ref }}-ios + cancel-in-progress: true + +jobs: + build: + if: github.ref_name == github.event.repository.default_branch + runs-on: macOS-11 + steps: + - uses: actions/checkout@v3 + - name: Compile iOS build + id: compile + run: | + cd ios + xcodebuild -sdk iphoneos -configuration Release CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + cd build/Release-iphoneos + mkdir Payload + mv ClassiCube.app Payload/ClassiCube.app + zip -r cc.ipa Payload + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile iOS build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ios/build/Release-iphoneos/cc.ipa' + DEST_NAME: 'cc.ipa' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'ios' \ No newline at end of file diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml new file mode 100644 index 000000000..d81af37f5 --- /dev/null +++ b/.github/workflows/build_linux.yml @@ -0,0 +1,110 @@ +name: Build latest (Linux) +on: [push] + +concurrency: + group: ${{ github.ref }}-linux + cancel-in-progress: true + +jobs: +#============================================ +# =============== 32 BIT LINUX ============== +# =========================================== + build-32: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + - name: Install packages + shell: bash + run: | + sudo dpkg --add-architecture i386 + sudo apt-get -y update + sudo apt-get -y install gcc-multilib libx11-dev:i386 libxi-dev:i386 libgl1-mesa-dev:i386 + - name: Compile 32 bit Linux builds + shell: bash + id: compile + env: + COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" + NIX32_FLAGS: "-no-pie -fno-pie -m32 -fvisibility=hidden -fcf-protection=none -rdynamic -DCC_BUILD_ICON" + run: | + LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" + + cd src + gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX32_FLAGS }} $LATEST_FLAG -o cc-nix32-gl1 -lX11 -lXi -lpthread -lGL -lm -ldl + gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX32_FLAGS }} $LATEST_FLAG -DCC_BUILD_GLMODERN -o cc-nix32-gl2 -lX11 -lXi -lpthread -lGL -lm -ldl + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile 32 bit Linux build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-nix32-gl1' + DEST_NAME: 'ClassiCube-Linux32-OpenGL' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-nix32-gl2' + DEST_NAME: 'ClassiCube-Linux32-ModernGL' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'linux32' + +#============================================ +# =============== 64 BIT LINUX ============== +# =========================================== + build-64: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + - name: Compile 64 bit Linux builds + shell: bash + id: compile + env: + COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" + NIX64_FLAGS: "-no-pie -fno-pie -m64 -fvisibility=hidden -fcf-protection=none -rdynamic -DCC_BUILD_ICON" + run: | + sudo apt-get -y install libx11-dev libxi-dev libgl1-mesa-dev + LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" + + cd src + gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX64_FLAGS }} $LATEST_FLAG -o cc-nix64-gl1 -lX11 -lXi -lpthread -lGL -lm -ldl + gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX64_FLAGS }} $LATEST_FLAG -DCC_BUILD_GLMODERN -o cc-nix64-gl2 -lX11 -lXi -lpthread -lGL -lm -ldl + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile 64 bit Linux build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-nix64-gl1' + DEST_NAME: 'ClassiCube-Linux64-OpenGL' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-nix64-gl2' + DEST_NAME: 'ClassiCube-Linux64-ModernGL' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'linux64' \ No newline at end of file diff --git a/.github/workflows/build_linux32.yml b/.github/workflows/build_linux32.yml deleted file mode 100644 index 8b9736140..000000000 --- a/.github/workflows/build_linux32.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Build latest (Linux 32 bit) -on: [push] - -concurrency: - group: ${{ github.ref }}-linux32 - cancel-in-progress: true - -jobs: - build: - if: github.ref_name == github.event.repository.default_branch - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - name: Install packages - shell: bash - run: | - sudo dpkg --add-architecture i386 - sudo apt-get -y update - sudo apt-get -y install gcc-multilib libx11-dev:i386 libxi-dev:i386 libgl1-mesa-dev:i386 - - name: Compile 32 bit Linux builds - shell: bash - id: compile - env: - COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" - NIX32_FLAGS: "-no-pie -fno-pie -m32 -fvisibility=hidden -fcf-protection=none -rdynamic -DCC_BUILD_ICON" - run: | - LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" - - cd src - gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX32_FLAGS }} $LATEST_FLAG -o cc-nix32-gl1 -lX11 -lXi -lpthread -lGL -lm -ldl - gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX32_FLAGS }} $LATEST_FLAG -DCC_BUILD_GLMODERN -o cc-nix32-gl2 -lX11 -lXi -lpthread -lGL -lm -ldl - - - - uses: ./.github/actions/notify_failure - if: ${{ always() && steps.compile.outcome == 'failure' }} - with: - NOTIFY_MESSAGE: 'Failed to compile 32 bit Linux build' - WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-nix32-gl1' - DEST_NAME: 'ClassiCube-Linux32' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-nix32-gl2' - DEST_NAME: 'ClassiCube-Linux32-ModernGL' \ No newline at end of file diff --git a/.github/workflows/build_linux64.yml b/.github/workflows/build_linux64.yml deleted file mode 100644 index 9a50d67de..000000000 --- a/.github/workflows/build_linux64.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Build latest (Linux 64 bit) -on: [push] - -concurrency: - group: ${{ github.ref }}-linux64 - cancel-in-progress: true - -jobs: - build: - if: github.ref_name == github.event.repository.default_branch - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - name: Compile 64 bit Linux builds - shell: bash - id: compile - env: - COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" - NIX64_FLAGS: "-no-pie -fno-pie -m64 -fvisibility=hidden -fcf-protection=none -rdynamic -DCC_BUILD_ICON" - run: | - sudo apt-get -y install libx11-dev libxi-dev libgl1-mesa-dev - LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" - - cd src - gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX64_FLAGS }} $LATEST_FLAG -o cc-nix64-gl1 -lX11 -lXi -lpthread -lGL -lm -ldl - gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.NIX64_FLAGS }} $LATEST_FLAG -DCC_BUILD_GLMODERN -o cc-nix64-gl2 -lX11 -lXi -lpthread -lGL -lm -ldl - - - - uses: ./.github/actions/notify_failure - if: ${{ always() && steps.compile.outcome == 'failure' }} - with: - NOTIFY_MESSAGE: 'Failed to compile 64 bit Linux build' - WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-nix64-gl1' - DEST_NAME: 'ClassiCube-Linux64' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-nix64-gl2' - DEST_NAME: 'ClassiCube-Linux64-ModernGL' \ No newline at end of file diff --git a/.github/workflows/build_n64.yml b/.github/workflows/build_n64.yml index e9d08f591..3ba69268b 100644 --- a/.github/workflows/build_n64.yml +++ b/.github/workflows/build_n64.yml @@ -16,6 +16,8 @@ jobs: - name: Compile N64 build id: compile run: | + apt-get update + apt-get -y install curl REAL_DIR=`pwd` cd /tmp git clone -b opengl https://github.com/DragonMinded/libdragon.git --depth=1 @@ -33,8 +35,21 @@ jobs: WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'build-n64/ClassiCube-n64.elf' + DEST_NAME: 'ClassiCube-n64.elf' + - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-n64.z64' - DEST_NAME: 'ClassiCube-n64.z64' \ No newline at end of file + DEST_NAME: 'ClassiCube-n64.z64' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'n64' diff --git a/.github/workflows/build_nds.yml b/.github/workflows/build_nds.yml new file mode 100644 index 000000000..abb0ac7a1 --- /dev/null +++ b/.github/workflows/build_nds.yml @@ -0,0 +1,49 @@ +name: Build latest (NDS) +on: [push] + +concurrency: + group: ${{ github.ref }}-nds + cancel-in-progress: true + +jobs: + build-DS: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: skylyrac/blocksds:dev-latest + steps: + - uses: actions/checkout@v4 + - name: Compile NDS build + id: compile + run: | + apt-get -y install curl + export BLOCKSDS=/opt/blocksds/core + export BLOCKSDSEXT=/opt/blocksds/external + make ds + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile NDS build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube.nds' + DEST_NAME: 'ClassiCube.nds' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'build-nds/ClassiCube.elf' + DEST_NAME: 'ClassiCube-nds.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'nds' diff --git a/.github/workflows/build_netbsd.yml b/.github/workflows/build_netbsd.yml index 72fd9cf8e..2f63c22ee 100644 --- a/.github/workflows/build_netbsd.yml +++ b/.github/workflows/build_netbsd.yml @@ -46,4 +46,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'src/cc-netbsd64-gl1' - DEST_NAME: 'ClassiCube-NetBSD-64' \ No newline at end of file + DEST_NAME: 'ClassiCube-NetBSD-64' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'netbsd' \ No newline at end of file diff --git a/.github/workflows/build_ps2.yml b/.github/workflows/build_ps2.yml index f9a04645c..3fd5a69d0 100644 --- a/.github/workflows/build_ps2.yml +++ b/.github/workflows/build_ps2.yml @@ -16,7 +16,7 @@ jobs: - name: Compile PS2 build id: compile run: | - apk add make mpc1 + apk add make mpc1 curl make ps2 @@ -31,4 +31,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-ps2.elf' - DEST_NAME: 'ClassiCube-ps2.elf' \ No newline at end of file + DEST_NAME: 'ClassiCube-ps2.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'ps2' \ No newline at end of file diff --git a/.github/workflows/build_ps3.yml b/.github/workflows/build_ps3.yml index 1e1e22772..b0f4ddb75 100644 --- a/.github/workflows/build_ps3.yml +++ b/.github/workflows/build_ps3.yml @@ -10,33 +10,49 @@ jobs: if: github.ref_name == github.event.repository.default_branch runs-on: ubuntu-latest container: - image: akusiroyo/ps3sdk:latest + image: ghcr.io/classicube/minimal-psl1ght:latest steps: - uses: actions/checkout@v4 - name: Compile PS3 build id: compile run: | - pacman -S make --noconfirm - export PSL1GHT=/usr/local/ps3dev + apt-get update + apt-get install -y curl export PS3DEV=/usr/local/ps3dev + export PSL1GHT=/usr/local/ps3dev + export PATH=$PATH:$PS3DEV/bin + export PATH=$PATH:$PS3DEV/ppu/bin make ps3 - + - uses: ./.github/actions/notify_failure if: ${{ always() && steps.compile.outcome == 'failure' }} with: NOTIFY_MESSAGE: 'Failed to compile PS3 build' WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-PS3.elf' + DEST_NAME: 'ClassiCube-PS3.elf' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-PS3.self' + DEST_NAME: 'ClassiCube-PS3.self' - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: - SOURCE_FILE: 'ClassiCube-ps3.pkg' - DEST_NAME: 'ClassiCube-ps3.pkg' + SOURCE_FILE: 'ClassiCube-PS3.pkg' + DEST_NAME: 'ClassiCube-PS3.pkg' - - uses: ./.github/actions/upload_build + + - uses: ./.github/actions/notify_success if: ${{ always() && steps.compile.outcome == 'success' }} with: - SOURCE_FILE: 'ClassiCube-ps3.elf' - DEST_NAME: 'ClassiCube-ps3.elf' \ No newline at end of file + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'ps3' diff --git a/.github/workflows/build_psp.yml b/.github/workflows/build_psp.yml index d5be1abc9..639dac684 100644 --- a/.github/workflows/build_psp.yml +++ b/.github/workflows/build_psp.yml @@ -16,14 +16,10 @@ jobs: - name: Compile PSP build id: compile run: | + apk add curl curl-dev make psp - # otherwise notify_failure doesn't work - - name: Install curl when necessary - if: ${{ always() && steps.compile.outcome == 'failure' }} - run: apk add curl curl-dev - - uses: ./.github/actions/notify_failure if: ${{ always() && steps.compile.outcome == 'failure' }} with: @@ -41,4 +37,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-psp.elf' - DEST_NAME: 'ClassiCube-psp.elf' \ No newline at end of file + DEST_NAME: 'ClassiCube-psp.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'psp' \ No newline at end of file diff --git a/.github/workflows/build_rpi.yml b/.github/workflows/build_rpi.yml new file mode 100644 index 000000000..c7da3f8e1 --- /dev/null +++ b/.github/workflows/build_rpi.yml @@ -0,0 +1,104 @@ +name: Build latest (RPI) +on: [push] + +concurrency: + group: ${{ github.ref }}-rpi + cancel-in-progress: true + +jobs: +#============================================ +# ================ 32 BIT RPI =============== +# =========================================== + build-RPI32: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: dockcross/linux-armv6-lts + steps: + - uses: actions/checkout@v4 + - name: Retrieve OpenGL and X11 dev files + run: | + mkdir src/rpi + cd src/rpi + wget https://github.com/ClassiCube/rpi-compiling-stuff/raw/main/rpi32.zip + unzip rpi32.zip + - name: Compile RPI 32 bit build + id: compile + env: + COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" + RPI32_FLAGS: "-fvisibility=hidden -rdynamic -DCC_BUILD_ICON -DCC_BUILD_RPI -I rpi/include -L rpi/lib -Wl,--unresolved-symbols=ignore-in-shared-libs" + run: | + LATEST_FLAG=-DCC_COMMIT_SHA=\"$GITHUB_SHA\" + + cd src + $CC *.c ${{ env.COMMON_FLAGS }} ${{ env.RPI32_FLAGS }} $LATEST_FLAG -o cc-rpi32 -lGLESv2 -lEGL -lX11 -lXi -lm -lpthread -ldl -lrt + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile RPI 32 bit build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-rpi32' + DEST_NAME: 'cc-rpi32' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'rpi32' + + +#============================================ +# ================ 64 BIT RPI =============== +# =========================================== + build-RPI64: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: dockcross/linux-arm64-lts + steps: + - uses: actions/checkout@v4 + - name: Retrieve OpenGL and X11 dev files + run: | + mkdir src/rpi + cd src/rpi + wget https://github.com/ClassiCube/rpi-compiling-stuff/raw/main/rpi64.zip + unzip rpi64.zip + - name: Compile RPI 64 bit build + id: compile + env: + COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" + RPI64_FLAGS: "-fvisibility=hidden -rdynamic -DCC_BUILD_ICON -DCC_BUILD_RPI -I rpi/include -L rpi/lib -Wl,--unresolved-symbols=ignore-in-shared-libs" + run: | + LATEST_FLAG=-DCC_COMMIT_SHA=\"$GITHUB_SHA\" + + cd src + $CC *.c ${{ env.COMMON_FLAGS }} ${{ env.RPI64_FLAGS }} $LATEST_FLAG -o cc-rpi64 -lGLESv2 -lEGL -lX11 -lXi -lm -lpthread -ldl -lrt + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile RPI 64 bit build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-rpi64' + DEST_NAME: 'cc-rpi64' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'rpi64' \ No newline at end of file diff --git a/.github/workflows/build_rpi32.yml b/.github/workflows/build_rpi32.yml deleted file mode 100644 index 8aa73ee8b..000000000 --- a/.github/workflows/build_rpi32.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build latest (RPI 32 bit) -on: [push] - -concurrency: - group: ${{ github.ref }}-rpi32 - cancel-in-progress: true - -jobs: - build-RPI32: - if: github.ref_name == github.event.repository.default_branch - runs-on: ubuntu-latest - container: - image: dockcross/linux-armv6-lts - steps: - - uses: actions/checkout@v4 - - name: Retrieve OpenGL and X11 dev files - run: | - mkdir src/rpi - cd src/rpi - wget https://github.com/ClassiCube/rpi-compiling-stuff/raw/main/rpi32.zip - unzip rpi32.zip - - name: Compile RPI 32 bit build - id: compile - env: - COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" - RPI32_FLAGS: "-fvisibility=hidden -rdynamic -DCC_BUILD_ICON -DCC_BUILD_RPI -I rpi/include -L rpi/lib -Wl,--unresolved-symbols=ignore-in-shared-libs" - run: | - LATEST_FLAG=-DCC_COMMIT_SHA=\"$GITHUB_SHA\" - - cd src - $CC *.c ${{ env.COMMON_FLAGS }} ${{ env.RPI32_FLAGS }} $LATEST_FLAG -o cc-rpi32 -lGLESv2 -lEGL -lX11 -lXi -lm -lpthread -ldl -lrt - - - - uses: ./.github/actions/notify_failure - if: ${{ always() && steps.compile.outcome == 'failure' }} - with: - NOTIFY_MESSAGE: 'Failed to compile RPI 32 bit build' - WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-rpi32' - DEST_NAME: 'cc-rpi32' \ No newline at end of file diff --git a/.github/workflows/build_rpi64.yml b/.github/workflows/build_rpi64.yml deleted file mode 100644 index f2ac06dc7..000000000 --- a/.github/workflows/build_rpi64.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Build latest (RPI 64 bit) -on: [push] - -concurrency: - group: ${{ github.ref }}-rpi64 - cancel-in-progress: true - -jobs: - build-RPI64: - if: github.ref_name == github.event.repository.default_branch - runs-on: ubuntu-latest - container: - image: dockcross/linux-arm64-lts - steps: - - uses: actions/checkout@v4 - - name: Retrieve OpenGL and X11 dev files - run: | - mkdir src/rpi - cd src/rpi - wget https://github.com/ClassiCube/rpi-compiling-stuff/raw/main/rpi64.zip - unzip rpi64.zip - - name: Compile RPI 64 bit build - id: compile - env: - COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" - RPI64_FLAGS: "-fvisibility=hidden -rdynamic -DCC_BUILD_ICON -DCC_BUILD_RPI -I rpi/include -L rpi/lib -Wl,--unresolved-symbols=ignore-in-shared-libs" - run: | - LATEST_FLAG=-DCC_COMMIT_SHA=\"$GITHUB_SHA\" - - cd src - $CC *.c ${{ env.COMMON_FLAGS }} ${{ env.RPI64_FLAGS }} $LATEST_FLAG -o cc-rpi64 -lGLESv2 -lEGL -lX11 -lXi -lm -lpthread -ldl -lrt - - - - uses: ./.github/actions/notify_failure - if: ${{ always() && steps.compile.outcome == 'failure' }} - with: - NOTIFY_MESSAGE: 'Failed to compile RPI 64 bit build' - WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-rpi64' - DEST_NAME: 'cc-rpi64' \ No newline at end of file diff --git a/.github/workflows/build_saturn.yml b/.github/workflows/build_saturn.yml new file mode 100644 index 000000000..59de7c6f3 --- /dev/null +++ b/.github/workflows/build_saturn.yml @@ -0,0 +1,38 @@ +name: Build latest (Saturn) +on: [push] + +concurrency: + group: ${{ github.ref }}-saturn + cancel-in-progress: true + +jobs: + build: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: ijacquez/yaul + steps: + - uses: actions/checkout@v4 + - name: Compile Saturn build + id: compile + run: | + make saturn + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile Saturn build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'build-saturn/ClassiCube-saturn.elf' + DEST_NAME: 'ClassiCube-saturn.elf' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-saturn.iso' + DEST_NAME: 'ClassiCube-saturn.iso' \ No newline at end of file diff --git a/.github/workflows/build_switch.yml b/.github/workflows/build_switch.yml new file mode 100644 index 000000000..a85a66a47 --- /dev/null +++ b/.github/workflows/build_switch.yml @@ -0,0 +1,46 @@ +name: Build latest (Switch) +on: [push] + +concurrency: + group: ${{ github.ref }}-switch + cancel-in-progress: true + +jobs: + build-switch: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: devkitpro/devkita64:latest + steps: + - uses: actions/checkout@v4 + - name: Compile Switch build + id: compile + run: | + make switch + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile Switch build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-switch.nro' + DEST_NAME: 'ClassiCube-switch.nro' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-switch.elf' + DEST_NAME: 'ClassiCube-switch.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'switch' \ No newline at end of file diff --git a/.github/workflows/build_vita.yml b/.github/workflows/build_vita.yml index 10d5a54f2..96195fed8 100644 --- a/.github/workflows/build_vita.yml +++ b/.github/workflows/build_vita.yml @@ -10,9 +10,9 @@ jobs: if: github.ref_name == github.event.repository.default_branch runs-on: ubuntu-latest container: - image: gnuton/vitasdk-docker:latest + image: vitasdk/vitasdk:latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile Vita build id: compile run: | @@ -36,4 +36,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-vita.elf' - DEST_NAME: 'ClassiCube-vita.elf' \ No newline at end of file + DEST_NAME: 'ClassiCube-vita.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'vita' \ No newline at end of file diff --git a/.github/workflows/build_wiigc.yml b/.github/workflows/build_wiigc.yml index c9286815c..833e0f97c 100644 --- a/.github/workflows/build_wiigc.yml +++ b/.github/workflows/build_wiigc.yml @@ -63,4 +63,11 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'wiitest' - DEST_NAME: 'ClassiCube-Wii-Homebrew' \ No newline at end of file + DEST_NAME: 'ClassiCube-Wii-Homebrew' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'wiigc' \ No newline at end of file diff --git a/.github/workflows/build_wiiu.yml b/.github/workflows/build_wiiu.yml new file mode 100644 index 000000000..e225d7a59 --- /dev/null +++ b/.github/workflows/build_wiiu.yml @@ -0,0 +1,52 @@ +name: Build latest (WiiU) +on: [push] + +concurrency: + group: ${{ github.ref }}-wiiu + cancel-in-progress: true + +jobs: + build: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: devkitpro/devkitppc:latest + steps: + - uses: actions/checkout@v4 + - name: Compile Wii U build + id: compile + run: | + make wiiu + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile WiiU build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-wiiu.wuhb' + DEST_NAME: 'ClassiCube-wiiu.wuhb' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-wiiu.rpx' + DEST_NAME: 'ClassiCube-wiiu.rpx' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-wiiu.elf' + DEST_NAME: 'ClassiCube-wiiu.elf' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'wiiu' \ No newline at end of file diff --git a/.github/workflows/build_win32.yml b/.github/workflows/build_win32.yml deleted file mode 100644 index cc79dd176..000000000 --- a/.github/workflows/build_win32.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Build latest (Windows 32 bit) -on: [push] - -concurrency: - group: ${{ github.ref }}-win32 - cancel-in-progress: true - -jobs: - build: - if: github.ref_name == github.event.repository.default_branch - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Compile 32 bit Windows builds - shell: bash - id: compile - env: - COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" - WIN32_FLAGS: "-mwindows -nostartfiles -Wl,-e_main_real -DCC_NOMAIN" - run: | - sudo apt-get -y install gcc-mingw-w64-i686 - LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" - - cp misc/windows/CCicon_32.res src/CCicon_32.res - cd src - i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -o cc-w32-d3d9.exe CCicon_32.res $LATEST_FLAG -lwinmm -limagehlp - i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -o cc-w32-ogl.exe CCicon_32.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_GL -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp -lopengl32 - i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -o cc-w32-d3d11.exe CCicon_32.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_D3D11 -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp - - # mingw defaults to i686, but some really old CPUs only support i586 - i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -march=i586 -o cc-w9x-ogl.exe CCicon_32.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_GL -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp -lopengl32 - - - - uses: ./.github/actions/notify_failure - if: ${{ always() && steps.compile.outcome == 'failure' }} - with: - NOTIFY_MESSAGE: 'Failed to compile 32 bit Windows build' - WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w32-d3d9.exe' - DEST_NAME: 'ClassiCube-Win32.exe' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w32-ogl.exe' - DEST_NAME: 'ClassiCube-Win32-OpenGL.exe' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w32-d3d11.exe' - DEST_NAME: 'ClassiCube-Win32-Direct3D11.exe' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w9x-ogl.exe' - DEST_NAME: 'ClassiCube-Win9x.exe' \ No newline at end of file diff --git a/.github/workflows/build_win64.yml b/.github/workflows/build_win64.yml deleted file mode 100644 index af670c9da..000000000 --- a/.github/workflows/build_win64.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Build latest (Windows 64 bit) -on: [push] - -concurrency: - group: ${{ github.ref }}-win64 - cancel-in-progress: true - -jobs: - build: - if: github.ref_name == github.event.repository.default_branch - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Compile 64 bit Windows builds - shell: bash - id: compile - env: - COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" - WIN64_FLAGS: "-mwindows -nostartfiles -Wl,-emain_real -DCC_NOMAIN" - run: | - sudo apt-get -y install gcc-mingw-w64-x86-64 - LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" - - cp misc/windows/CCicon_64.res src/CCicon_64.res - cd src - x86_64-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN64_FLAGS }} -o cc-w64-d3d9.exe CCicon_64.res $LATEST_FLAG -lwinmm -limagehlp - x86_64-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN64_FLAGS }} -o cc-w64-ogl.exe CCicon_64.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_GL -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp -lopengl32 - x86_64-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN64_FLAGS }} -o cc-w64-d3d11.exe CCicon_64.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_D3D11 -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp - - - - uses: ./.github/actions/notify_failure - if: ${{ always() && steps.compile.outcome == 'failure' }} - with: - NOTIFY_MESSAGE: 'Failed to compile 64 bit Windows build' - WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w64-d3d9.exe' - DEST_NAME: 'ClassiCube-Win64.exe' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w64-ogl.exe' - DEST_NAME: 'ClassiCube-Win64-OpenGL.exe' - - - uses: ./.github/actions/upload_build - if: ${{ always() && steps.compile.outcome == 'success' }} - with: - SOURCE_FILE: 'src/cc-w64-d3d11.exe' - DEST_NAME: 'ClassiCube-Win64-Direct3D11.exe' \ No newline at end of file diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml new file mode 100644 index 000000000..cc14cfe70 --- /dev/null +++ b/.github/workflows/build_windows.yml @@ -0,0 +1,131 @@ +name: Build latest (Windows) +on: [push] + +concurrency: + group: ${{ github.ref }}-windows + cancel-in-progress: true + +jobs: +#============================================ +# ============== 32 BIT WINDOWS ============= +# =========================================== + build-32: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Compile 32 bit Windows builds + shell: bash + id: compile + env: + COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" + WIN32_FLAGS: "-mwindows -nostartfiles -Wl,-e_main_real -DCC_NOMAIN" + run: | + sudo apt-get -y install gcc-mingw-w64-i686 + LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" + + cp misc/windows/CCicon_32.res src/CCicon_32.res + cd src + i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -o cc-w32-d3d9.exe CCicon_32.res $LATEST_FLAG -lwinmm -limagehlp + i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -o cc-w32-ogl.exe CCicon_32.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_GL -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp -lopengl32 + i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -o cc-w32-d3d11.exe CCicon_32.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_D3D11 -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp + + # mingw defaults to i686, but some really old CPUs only support i586 + i686-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN32_FLAGS }} -march=i586 -o cc-w9x-ogl.exe CCicon_32.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_GL -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp -lopengl32 + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile 32 bit Windows build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w32-d3d9.exe' + DEST_NAME: 'ClassiCube-Win32-Direct3D9.exe' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w32-ogl.exe' + DEST_NAME: 'ClassiCube-Win32-OpenGL.exe' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w32-d3d11.exe' + DEST_NAME: 'ClassiCube-Win32-Direct3D11.exe' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w9x-ogl.exe' + DEST_NAME: 'ClassiCube-Win9x.exe' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'win32' + + +#============================================ +# ============== 64 BIT WINDOWS ============= +# =========================================== + build-64: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Compile 64 bit Windows builds + shell: bash + id: compile + env: + COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn" + WIN64_FLAGS: "-mwindows -nostartfiles -Wl,-emain_real -DCC_NOMAIN" + run: | + sudo apt-get -y install gcc-mingw-w64-x86-64 + LATEST_FLAG=-DCC_COMMIT_SHA=\"$(git rev-parse --short "$GITHUB_SHA")\" + + cp misc/windows/CCicon_64.res src/CCicon_64.res + cd src + x86_64-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN64_FLAGS }} -o cc-w64-d3d9.exe CCicon_64.res $LATEST_FLAG -lwinmm -limagehlp + x86_64-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN64_FLAGS }} -o cc-w64-ogl.exe CCicon_64.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_GL -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp -lopengl32 + x86_64-w64-mingw32-gcc *.c ${{ env.COMMON_FLAGS }} ${{ env.WIN64_FLAGS }} -o cc-w64-d3d11.exe CCicon_64.res $LATEST_FLAG -DCC_BUILD_MANUAL -DCC_BUILD_WIN -DCC_BUILD_D3D11 -DCC_BUILD_WINGUI -DCC_BUILD_WGL -DCC_BUILD_WINMM -DCC_BUILD_HTTPCLIENT -DCC_BUILD_SCHANNEL -lwinmm -limagehlp + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile 64 bit Windows build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w64-d3d9.exe' + DEST_NAME: 'ClassiCube-Win64-Direct3D9.exe' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w64-ogl.exe' + DEST_NAME: 'ClassiCube-Win64-OpenGL.exe' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'src/cc-w64-d3d11.exe' + DEST_NAME: 'ClassiCube-Win64-Direct3D11.exe' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'win64' \ No newline at end of file diff --git a/.github/workflows/build_xbox.yml b/.github/workflows/build_xbox.yml index 9245b29b8..9c8b9ebee 100644 --- a/.github/workflows/build_xbox.yml +++ b/.github/workflows/build_xbox.yml @@ -16,15 +16,12 @@ jobs: - name: Compile Xbox build id: compile run: | + apk add curl curl-dev eval $(/usr/src/nxdk/bin/activate -s) make xbox + cp bin/default.xbe ClassiCube-xbox.xbe - # otherwise notify_failure doesn't work - - name: Install curl when necessary - if: ${{ always() && steps.compile.outcome == 'failure' }} - run: apk add curl curl-dev - - uses: ./.github/actions/notify_failure if: ${{ always() && steps.compile.outcome == 'failure' }} with: @@ -35,11 +32,18 @@ jobs: - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: - SOURCE_FILE: 'bin/default.xbe' + SOURCE_FILE: 'ClassiCube-xbox.xbe' DEST_NAME: 'ClassiCube-xbox.xbe' - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-xbox.iso' - DEST_NAME: 'ClassiCube-xbox.iso' \ No newline at end of file + DEST_NAME: 'ClassiCube-xbox.iso' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'xbox' \ No newline at end of file diff --git a/.github/workflows/build_xbox360.yml b/.github/workflows/build_xbox360.yml new file mode 100644 index 000000000..8b07cbc7a --- /dev/null +++ b/.github/workflows/build_xbox360.yml @@ -0,0 +1,50 @@ +name: Build latest (Xbox 360) +on: [push] + +concurrency: + group: ${{ github.ref }}-xbox360 + cancel-in-progress: true + +jobs: + build-Xbox: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + container: + image: free60/libxenon + steps: + - uses: actions/checkout@v3 + - name: Compile 360 build + id: compile + run: | + sed -i -e 's/archive.ubuntu.com\|security.ubuntu.com/old-releases.ubuntu.com/g' /etc/apt/sources.list + apt-get update + apt-get install -y curl + export DEVKITXENON=/usr/local/xenon + export PATH=$PATH:$DEVKITXENON/bin:$DEVKITXENON/usr/bin + make xbox360 + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to compile Xbox 360 build' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-xbox360.elf' + DEST_NAME: 'ClassiCube-xbox360.elf' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'ClassiCube-xbox360.elf32' + DEST_NAME: 'ClassiCube-xbox360.elf32' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'xbox360' \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..cbf07cf1a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,179 @@ +name: Produce release +on: [workflow_dispatch] + +concurrency: + group: ${{ github.ref }}-release + cancel-in-progress: true + +jobs: + build: + if: github.ref_name == github.event.repository.default_branch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # Download resources + - name: Retrieve classicube texture pack + run: | + wget https://www.classicube.net/static/default.zip + - name: Retrieve classicube audio pack + run: | + wget https://www.classicube.net/static/audio.zip + + # Download windows artifacts + - name: Retrieve Windows binaries + run: | + wget https://cdn.classicube.net/client/latest/ClassiCube.64.exe -O cc-w64.exe + wget https://cdn.classicube.net/client/latest/ClassiCube.exe -O cc-w32.exe + + # Download Linux artifacts + - name: Retrieve Linux binaries + run: | + wget https://cdn.classicube.net/client/latest/ClassiCube -O cc-linux-64 + wget https://cdn.classicube.net/client/latest/ClassiCube.32 -O cc-linux-32 + + # Download macOS artifacts + - name: Retrieve macOS binaries + run: | + wget https://cdn.classicube.net/client/latest/ClassiCube.64.osx -O cc-mac-64 + wget https://cdn.classicube.net/client/latest/ClassiCube.osx -O cc-mac-32 + + # Download RPI artifacts + - name: Retrieve RPI binaries + run: | + wget https://cdn.classicube.net/client/latest/cc-rpi64 -O cc-rpi-64 + wget https://cdn.classicube.net/client/latest/ClassiCube.rpi -O cc-rpi-32 + + # Download FreeBSD artifacts + - name: Retrieve macOS binaries + run: | + wget https://cdn.classicube.net/client/latest/cc-freebsd-64 -O cc-freebsd-64 + wget https://cdn.classicube.net/client/latest/cc-freebsd-32 -O cc-freebsd-32 + + - name: Generate builds + id: compile + shell: bash + run: | + mkdir ClassiCube + mkdir ClassiCube/audio + mkdir ClassiCube/texpacks + cp audio.zip ClassiCube/audio/classicube.zip + cp default.zip ClassiCube/texpacks/classicube.zip + + # ./ClassiCube + make_unix_tar() { + cp $2 ClassiCube/ClassiCube + chmod +x ClassiCube/ClassiCube + tar -zcvf $1 ClassiCube + rm ClassiCube/ClassiCube + } + + # ./ClassiCube + make_windows_zip() { + cp $2 ClassiCube/ClassiCube.exe + zip -r $1 ClassiCube + rm ClassiCube/ClassiCube.exe + } + + # Generate FreeBSD builds + make_unix_tar cc-freebsd32.tar.gz cc-freebsd-32 + make_unix_tar cc-freebsd64.tar.gz cc-freebsd-64 + + # Generate RPI builds + make_unix_tar cc-rpi32.tar.gz cc-rpi-32 + make_unix_tar cc-rpi64.tar.gz cc-rpi-64 + + # Generate Linux builds + make_unix_tar cc-linux32.tar.gz cc-linux-32 + make_unix_tar cc-linux64.tar.gz cc-linux-64 + + # Generate macOS builds + make_unix_tar cc-mac32.tar.gz cc-mac-32 + make_unix_tar cc-mac64.tar.gz cc-mac-64 + + # Generate Windows builds + make_windows_zip cc-win32.zip cc-w32.exe + make_windows_zip cc-win64.zip cc-w64.exe + + + - uses: ./.github/actions/notify_failure + if: ${{ always() && steps.compile.outcome == 'failure' }} + with: + NOTIFY_MESSAGE: 'Failed to produce release' + WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' + + + # Generate Linux release files + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-linux32.tar.gz' + DEST_NAME: 'cc-linux32.tar.gz' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-linux64.tar.gz' + DEST_NAME: 'cc-linux64.tar.gz' + + + # Generate macOS release files + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-mac32.tar.gz' + DEST_NAME: 'cc-mac32.tar.gz' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-mac64.tar.gz' + DEST_NAME: 'cc-mac64.tar.gz' + + + # Generate Windows release files + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-win32.zip' + DEST_NAME: 'cc-win32.zip' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-win64.zip' + DEST_NAME: 'cc-win64.zip' + + + # Generate RPI release files + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-rpi32.tar.gz' + DEST_NAME: 'cc-rpi32.tar.gz' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-rpi64.tar.gz' + DEST_NAME: 'cc-rpi64.tar.gz' + + + # Generate FreeBSD release files + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-freebsd32.tar.gz' + DEST_NAME: 'cc-freebsd32.tar.gz' + + - uses: ./.github/actions/upload_build + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + SOURCE_FILE: 'cc-freebsd64.tar.gz' + DEST_NAME: 'cc-freebsd64.tar.gz' + + + - uses: ./.github/actions/notify_success + if: ${{ always() && steps.compile.outcome == 'success' }} + with: + DESTINATION_URL: '${{ secrets.NOTIFY_URL }}' + WORKFLOW_NAME: 'release' \ No newline at end of file diff --git a/.gitignore b/.gitignore index eb9e6f85c..48865746a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,16 +20,38 @@ android/app/.externalNativeBuild/ android/local.properties *.iml -# Console build results -build-360/ +# Nintendo Console build results build-3ds/ -build-dc/ build-gc/ +build-nds/ build-n64/ +build-wii/ +build-wiiu/ +build-switch/ +classicube.nds +# SEGA console build results +build-dc/ +IP.BIN +ISO_FILES/ +third_party/gldc/libGLdc.a +build-saturn/ +cd/ +# Microsoft console build results +build-360/ +main.exe +main.lib +misc/xbox/ps_coloured.inl +misc/xbox/ps_textured.inl +misc/xbox/vs_coloured.inl +misc/xbox/vs_textured.inl +# Sony console build results build-ps2/ build-ps3/ +build-psp/ build-vita/ -build-wii/ +eboot.pbp +eboot.bin +param.sfo # Build results [Dd]ebug/ @@ -66,6 +88,13 @@ ClassiCube* screenshots fontscache.txt +# Android source files need to be included +!android/app/src/main/java/com/classicube + +# CMake files +CMakeFiles/ +CMakeCache.txt + #GCC object files *.o diff --git a/Makefile b/Makefile index ffab56141..a5954689b 100644 --- a/Makefile +++ b/Makefile @@ -1,99 +1,109 @@ -C_SOURCES:=$(wildcard src/*.c) -C_OBJECTS:=$(patsubst %.c, %.o, $(C_SOURCES)) -OBJECTS:=$(C_OBJECTS) -ENAME=ClassiCube -DEL=rm -f -CFLAGS=-g -pipe -fno-math-errno -LDFLAGS=-g -rdynamic +SOURCE_DIR := src +BUILD_DIR := build +C_SOURCES := $(wildcard $(SOURCE_DIR)/*.c) +C_OBJECTS := $(patsubst $(SOURCE_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES)) + +OBJECTS := $(C_OBJECTS) +ENAME = ClassiCube +DEL = rm -f +CFLAGS = -g -pipe -fno-math-errno +LDFLAGS = -g -rdynamic + +# Enables dependency tracking (https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/) +# This ensures that changing a .h file automatically results in the .c files using it being auto recompiled when next running make +# On older systems the required GCC options may not be supported - in which case just change TRACK_DEPENDENCIES to 0 +TRACK_DEPENDENCIES=1 ifndef $(PLAT) ifeq ($(OS),Windows_NT) - PLAT=mingw + PLAT = mingw else - PLAT=$(shell uname -s | tr '[:upper:]' '[:lower:]') + PLAT = $(shell uname -s | tr '[:upper:]' '[:lower:]') endif endif ifeq ($(PLAT),web) -CC=emcc -OEXT=.html -CFLAGS=-g -LDFLAGS=-s WASM=1 -s NO_EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=1Mb --js-library src/interop_web.js + CC = emcc + OEXT = .html + CFLAGS = -g + LDFLAGS = -s WASM=1 -s NO_EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=1Mb --js-library $(SOURCE_DIR)/interop_web.js endif ifeq ($(PLAT),mingw) -CC=gcc -OEXT=.exe -CFLAGS=-g -pipe -DUNICODE -fno-math-errno -LDFLAGS=-g -LIBS=-mwindows -lwinmm -limagehlp + CC = gcc + OEXT = .exe + CFLAGS = -g -pipe -DUNICODE -fno-math-errno + LDFLAGS = -g + LIBS = -mwindows -lwinmm -limagehlp endif ifeq ($(PLAT),linux) -LIBS=-lX11 -lXi -lpthread -lGL -ldl + CFLAGS = -g -pipe -fno-math-errno -DCC_BUILD_ICON + LIBS = -lX11 -lXi -lpthread -lGL -ldl endif ifeq ($(PLAT),sunos) -CFLAGS=-g -pipe -fno-math-errno -LIBS=-lsocket -lX11 -lXi -lGL + CFLAGS = -g -pipe -fno-math-errno + LIBS = -lsocket -lX11 -lXi -lGL endif ifeq ($(PLAT),darwin) -OBJECTS+=src/interop_cocoa.o -CFLAGS=-g -pipe -fno-math-errno -LIBS= -LDFLAGS=-rdynamic -framework Cocoa -framework OpenGL -framework IOKit -lobjc + OBJECTS += $(BUILD_DIR)/interop_cocoa.o + CFLAGS = -g -pipe -fno-math-errno -DCC_BUILD_ICON + LIBS = + LDFLAGS = -rdynamic -framework Cocoa -framework OpenGL -framework IOKit -lobjc endif ifeq ($(PLAT),freebsd) -CFLAGS=-g -pipe -I /usr/local/include -fno-math-errno -LDFLAGS=-L /usr/local/lib -rdynamic -LIBS=-lexecinfo -lGL -lX11 -lXi -lpthread + CFLAGS = -g -pipe -I /usr/local/include -fno-math-errno -DCC_BUILD_ICON + LDFLAGS = -L /usr/local/lib -rdynamic + LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread endif ifeq ($(PLAT),openbsd) -CFLAGS=-g -pipe -I /usr/X11R6/include -I /usr/local/include -fno-math-errno -LDFLAGS=-L /usr/X11R6/lib -L /usr/local/lib -rdynamic -LIBS=-lexecinfo -lGL -lX11 -lXi -lpthread + CFLAGS = -g -pipe -I /usr/X11R6/include -I /usr/local/include -fno-math-errno -DCC_BUILD_ICON + LDFLAGS = -L /usr/X11R6/lib -L /usr/local/lib -rdynamic + LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread endif ifeq ($(PLAT),netbsd) -CFLAGS=-g -pipe -I /usr/X11R7/include -I /usr/pkg/include -fno-math-errno -LDFLAGS=-L /usr/X11R7/lib -L /usr/pkg/lib -rdynamic -LIBS=-lexecinfo -lGL -lX11 -lXi -lpthread + CFLAGS = -g -pipe -I /usr/X11R7/include -I /usr/pkg/include -fno-math-errno -DCC_BUILD_ICON + LDFLAGS = -L /usr/X11R7/lib -L /usr/pkg/lib -rdynamic + LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread endif ifeq ($(PLAT),dragonfly) -CFLAGS=-g -pipe -I /usr/local/include -fno-math-errno -LDFLAGS=-L /usr/local/lib -rdynamic -LIBS=-lexecinfo -lGL -lX11 -lXi -lpthread + CFLAGS = -g -pipe -I /usr/local/include -fno-math-errno -DCC_BUILD_ICON + LDFLAGS = -L /usr/local/lib -rdynamic + LIBS = -lexecinfo -lGL -lX11 -lXi -lpthread endif ifeq ($(PLAT),haiku) -OBJECTS+=src/interop_BeOS.o -CFLAGS=-g -pipe -fno-math-errno -LDFLAGS=-g -LIBS=-lGL -lnetwork -lstdc++ -lbe -lgame -ltracker + OBJECTS += $(BUILD_DIR)/interop_BeOS.o + CFLAGS = -g -pipe -fno-math-errno + LDFLAGS = -g + LIBS = -lGL -lnetwork -lstdc++ -lbe -lgame -ltracker endif ifeq ($(PLAT),beos) -OBJECTS+=src/interop_BeOS.o -CFLAGS=-g -pipe -LDFLAGS=-g -LIBS=-lGL -lnetwork -lstdc++ -lbe -lgame -ltracker + OBJECTS += $(BUILD_DIR)/interop_BeOS.o + CFLAGS = -g -pipe + LDFLAGS = -g + LIBS = -lGL -lnetwork -lstdc++ -lbe -lgame -ltracker + TRACK_DEPENDENCIES=0 endif ifeq ($(PLAT),serenityos) -LIBS=-lgl -lSDL2 + LIBS = -lgl -lSDL2 endif ifeq ($(PLAT),irix) -CC=gcc -LIBS=-lGL -lX11 -lXi -lpthread -ldl + CC = gcc + LIBS = -lGL -lX11 -lXi -lpthread -ldl endif ifeq ($(OS),Windows_NT) -DEL=del + DEL = del endif default: $(PLAT) @@ -124,43 +134,75 @@ serenityos: $(MAKE) $(ENAME) PLAT=serenityos irix: $(MAKE) $(ENAME) PLAT=irix - + # consoles builds require special handling, so are moved to # separate makefiles to avoid having one giant messy makefile -psp: - $(MAKE) -f misc/psp/Makefile PLAT=psp -vita: - $(MAKE) -f misc/vita/Makefile PLAT=vita -ps3: - $(MAKE) -f misc/ps3/Makefile PLAT=ps3 -ps2: - $(MAKE) -f misc/ps2/Makefile PLAT=ps2 -3ds: - $(MAKE) -f misc/3ds/Makefile PLAT=3ds -wii: - $(MAKE) -f misc/wii/Makefile PLAT=wii -gamecube: - $(MAKE) -f misc/gc/Makefile PLAT=gamecube dreamcast: - $(MAKE) -f misc/dreamcast/Makefile PLAT=dreamcast + $(MAKE) -f misc/dreamcast/Makefile +saturn: + $(MAKE) -f misc/saturn/Makefile +psp: + $(MAKE) -f misc/psp/Makefile +vita: + $(MAKE) -f misc/vita/Makefile +ps1: + cmake --preset default misc/ps1 + cmake --build misc/ps1/build +ps2: + $(MAKE) -f misc/ps2/Makefile +ps3: + $(MAKE) -f misc/ps3/Makefile xbox: - $(MAKE) -f misc/xbox/Makefile PLAT=xbox + $(MAKE) -f misc/xbox/Makefile xbox360: - $(MAKE) -f misc/xbox360/Makefile PLAT=xbox360 + $(MAKE) -f misc/xbox360/Makefile n64: - $(MAKE) -f misc/n64/Makefile PLAT=n64 + $(MAKE) -f misc/n64/Makefile +ds: + $(MAKE) -f misc/ds/Makefile +3ds: + $(MAKE) -f misc/3ds/Makefile +gamecube: + $(MAKE) -f misc/gc/Makefile +wii: + $(MAKE) -f misc/wii/Makefile +wiiu: + $(MAKE) -f misc/wiiu/Makefile +switch: + $(MAKE) -f misc/switch/Makefile +os/2: + $(MAKE) -f misc/os2/Makefile clean: $(DEL) $(OBJECTS) -$(ENAME): $(OBJECTS) - $(CC) $(LDFLAGS) -o $@$(OEXT) $(OBJECTS) $(LIBS) -$(C_OBJECTS): %.o : %.c +$(ENAME): $(BUILD_DIR) $(OBJECTS) + $(CC) $(LDFLAGS) -o $@$(OEXT) $(OBJECTS) $(LIBS) +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + + +# === Compiling with dependency tracking === +# NOTE: Tracking dependencies might not work on older systems - disable this if so +ifeq ($(TRACK_DEPENDENCIES), 1) +DEPFLAGS = -MT $@ -MMD -MP -MF $(BUILD_DIR)/$*.d +DEPFILES := $(patsubst $(SOURCE_DIR)/%.c, $(BUILD_DIR)/%.d, $(C_SOURCES)) +$(DEPFILES): + +$(C_OBJECTS): $(BUILD_DIR)/%.o : $(SOURCE_DIR)/%.c $(BUILD_DIR)/%.d + $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ + +include $(wildcard $(DEPFILES)) +# === Compiling WITHOUT dependency tracking === +else +$(C_OBJECTS): $(BUILD_DIR)/%.o : $(SOURCE_DIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ +endif + +# === Platform specific file compiling === +$(BUILD_DIR)/interop_cocoa.o: $(SOURCE_DIR)/interop_cocoa.m $(CC) $(CFLAGS) -c $< -o $@ -src/interop_cocoa.o: src/interop_cocoa.m - $(CC) $(CFLAGS) -c $< -o $@ - -src/interop_BeOS.o: src/interop_BeOS.cpp +$(BUILD_DIR)/interop_BeOS.o: $(SOURCE_DIR)/interop_BeOS.cpp $(CC) $(CFLAGS) -c $< -o $@ diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index e52a98984..ef21c0e22 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -22,7 +22,7 @@ set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Werror") add_library(classicube SHARED - ../../src/Program.c + ../../src/main.c ../../src/IsometricDrawer.c ../../src/Builder.c ../../src/ExtMath.c @@ -99,6 +99,10 @@ add_library(classicube SHARED ../../src/LBackend.c ../../src/SystemFonts.c ../../src/Commands.c + ../../src/EntityRenderers.c + ../../src/AudioBackend.c + ../../src/TouchUI.c + ../../src/LBackend_Android.c ) # add lib dependencies @@ -107,4 +111,5 @@ target_link_libraries(classicube EGL GLESv2 log - OpenSLES) + OpenSLES + jnigraphics) diff --git a/android/app/src/main/java/com/classicube/CCView.java b/android/app/src/main/java/com/classicube/CCView.java new file mode 100644 index 000000000..d44563452 --- /dev/null +++ b/android/app/src/main/java/com/classicube/CCView.java @@ -0,0 +1,108 @@ +package com.classicube; +import android.text.Editable; +import android.text.Selection; +import android.text.SpannableStringBuilder; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +public class CCView extends SurfaceView { + SpannableStringBuilder kbText; + MainActivity activity; + + public CCView(MainActivity activity) { + // setFocusable, setFocusableInTouchMode - API level 1 + super(activity); + this.activity = activity; + setFocusable(true); + setFocusableInTouchMode(true); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return activity.handleTouchEvent(ev) || super.dispatchTouchEvent(ev); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo attrs) { + // BaseInputConnection, IME_ACTION_GO, IME_FLAG_NO_EXTRACT_UI - API level 3 + attrs.actionLabel = null; + attrs.inputType = MainActivity.calcKeyboardType(activity.keyboardType); + attrs.imeOptions = MainActivity.calcKeyboardOptions(activity.keyboardType); + + kbText = new SpannableStringBuilder(activity.keyboardText); + + InputConnection ic = new BaseInputConnection(this, true) { + boolean inited; + + void updateText() { + activity.pushCmd(MainActivity.CMD_KEY_TEXT, kbText.toString()); + } + + @Override + public Editable getEditable() { + if (!inited) { + // needed to set selection, otherwise random crashes later with backspacing + // set selection to end, so backspacing after opening keyboard with text still works + Selection.setSelection(kbText, kbText.toString().length()); + inited = true; + } + return kbText; + } + + @Override + public boolean setComposingText(CharSequence text, int newCursorPosition) { + boolean success = super.setComposingText(text, newCursorPosition); + updateText(); + return success; + } + + @Override + public boolean deleteSurroundingText(int beforeLength, int afterLength) { + boolean success = super.deleteSurroundingText(beforeLength, afterLength); + updateText(); + return success; + } + + @Override + public boolean commitText(CharSequence text, int newCursorPosition) { + boolean success = super.commitText(text, newCursorPosition); + updateText(); + return success; + } + + @Override + public boolean sendKeyEvent(KeyEvent ev) { + // getSelectionStart - API level 1 + if (ev.getAction() != KeyEvent.ACTION_DOWN) return super.sendKeyEvent(ev); + int code = ev.getKeyCode(); + int uni = ev.getUnicodeChar(); + + // start is -1 sometimes, and trying to insert/delete there crashes + int start = Selection.getSelectionStart(kbText); + if (start == -1) start = kbText.toString().length(); + + if (code == KeyEvent.KEYCODE_ENTER) { + // enter maps to \n but that should not be intercepted + } else if (code == KeyEvent.KEYCODE_DEL) { + if (start <= 0) return false; + kbText.delete(start - 1, start); + updateText(); + return false; + } else if (uni != 0) { + kbText.insert(start, String.valueOf((char) uni)); + updateText(); + return false; + } + return super.sendKeyEvent(ev); + } + + }; + //String text = MainActivity.this.keyboardText; + //if (text != null) ic.setComposingText(text, 0); + return ic; + } +} diff --git a/android/app/src/main/java/com/classicube/MainActivity.java b/android/app/src/main/java/com/classicube/MainActivity.java index f3a0a068e..2e39cdd81 100644 --- a/android/app/src/main/java/com/classicube/MainActivity.java +++ b/android/app/src/main/java/com/classicube/MainActivity.java @@ -57,6 +57,7 @@ import android.view.inputmethod.InputMethodManager; // implements InputQueue.Callback public class MainActivity extends Activity { + public boolean launcher; // ================================================================== // ---------------------------- COMMANDS ---------------------------- // ================================================================== @@ -72,21 +73,29 @@ public class MainActivity extends Activity NativeCmdArgs args = freeCmds.poll(); return args != null ? args : new NativeCmdArgs(); } - - void pushCmd(int cmd) { + + public void pushCmd(int cmd) { NativeCmdArgs args = getCmdArgs(); args.cmd = cmd; pending.add(args); } - - void pushCmd(int cmd, int a1) { + + public void pushCmd(int cmd, int a1) { NativeCmdArgs args = getCmdArgs(); args.cmd = cmd; args.arg1 = a1; pending.add(args); } + + public void pushCmd(int cmd, int a1, int a2) { + NativeCmdArgs args = getCmdArgs(); + args.cmd = cmd; + args.arg1 = a1; + args.arg2 = a2; + pending.add(args); + } - void pushCmd(int cmd, int a1, int a2, int a3, int a4) { + public void pushCmd(int cmd, int a1, int a2, int a3, int a4) { NativeCmdArgs args = getCmdArgs(); args.cmd = cmd; args.arg1 = a1; @@ -96,45 +105,58 @@ public class MainActivity extends Activity pending.add(args); } - void pushCmd(int cmd, String text) { + public void pushCmd(int cmd, String text) { NativeCmdArgs args = getCmdArgs(); args.cmd = cmd; args.str = text; pending.add(args); } - - void pushCmd(int cmd, Surface surface) { + + public void pushCmd(int cmd, int a1, String str) { + NativeCmdArgs args = getCmdArgs(); + args.cmd = cmd; + args.arg1 = a1; + args.str = str; + pending.add(args); + } + + public void pushCmd(int cmd, Surface surface) { NativeCmdArgs args = getCmdArgs(); args.cmd = cmd; args.sur = surface; pending.add(args); } - final static int CMD_KEY_DOWN = 0; - final static int CMD_KEY_UP = 1; - final static int CMD_KEY_CHAR = 2; - final static int CMD_POINTER_DOWN = 3; - final static int CMD_POINTER_UP = 4; - final static int CMD_POINTER_MOVE = 5; - - final static int CMD_WIN_CREATED = 6; - final static int CMD_WIN_DESTROYED = 7; - final static int CMD_WIN_RESIZED = 8; - final static int CMD_WIN_REDRAW = 9; + public final static int CMD_KEY_DOWN = 0; + public final static int CMD_KEY_UP = 1; + public final static int CMD_KEY_CHAR = 2; + public final static int CMD_POINTER_DOWN = 3; + public final static int CMD_POINTER_UP = 4; + public final static int CMD_POINTER_MOVE = 5; - final static int CMD_APP_START = 10; - final static int CMD_APP_STOP = 11; - final static int CMD_APP_RESUME = 12; - final static int CMD_APP_PAUSE = 13; - final static int CMD_APP_DESTROY = 14; + public final static int CMD_WIN_CREATED = 6; + public final static int CMD_WIN_DESTROYED = 7; + public final static int CMD_WIN_RESIZED = 8; + public final static int CMD_WIN_REDRAW = 9; - final static int CMD_GOT_FOCUS = 15; - final static int CMD_LOST_FOCUS = 16; - final static int CMD_CONFIG_CHANGED = 17; - final static int CMD_LOW_MEMORY = 18; + public final static int CMD_APP_START = 10; + public final static int CMD_APP_STOP = 11; + public final static int CMD_APP_RESUME = 12; + public final static int CMD_APP_PAUSE = 13; + public final static int CMD_APP_DESTROY = 14; - final static int CMD_KEY_TEXT = 19; - final static int CMD_OFD_RESULT = 20; + public final static int CMD_GOT_FOCUS = 15; + public final static int CMD_LOST_FOCUS = 16; + public final static int CMD_CONFIG_CHANGED = 17; + public final static int CMD_LOW_MEMORY = 18; + + public final static int CMD_KEY_TEXT = 19; + public final static int CMD_OFD_RESULT = 20; + + public final static int CMD_UI_CREATED = 21; + public final static int CMD_UI_CLICKED = 22; + public final static int CMD_UI_CHANGED = 23; + public final static int CMD_UI_STRING = 24; // ==================================================================== @@ -385,7 +407,15 @@ public class MainActivity extends Activity // static to persist across activity destroy/create static final Semaphore winDestroyedSem = new Semaphore(0, true); SurfaceHolder.Callback callback; - CCView curView; + public View curView; + + public void setActiveView(View view) { + // setContentView, requestFocus - API level 1 + curView = view; + setContentView(view); + curView.requestFocus(); + if (fullscreen) setUIVisibility(FULLSCREEN_FLAGS); + } // SurfaceHolder.Callback - API level 1 class CCSurfaceCallback implements SurfaceHolder.Callback { @@ -449,107 +479,11 @@ public class MainActivity extends Activity void attachSurface() { // setContentView, requestFocus, getHolder, addCallback, RGBX_8888 - API level 1 createSurfaceCallback(); - curView = new CCView(this); - curView.getHolder().addCallback(callback); - curView.getHolder().setFormat(PixelFormat.RGBX_8888); - - setContentView(curView); - curView.requestFocus(); - if (fullscreen) setUIVisibility(FULLSCREEN_FLAGS); - } + CCView view = new CCView(this); + view.getHolder().addCallback(callback); + view.getHolder().setFormat(PixelFormat.RGBX_8888); - class CCView extends SurfaceView { - SpannableStringBuilder kbText; - - public CCView(Context context) { - // setFocusable, setFocusableInTouchMode - API level 1 - super(context); - setFocusable(true); - setFocusableInTouchMode(true); - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - return MainActivity.this.handleTouchEvent(ev) || super.dispatchTouchEvent(ev); - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo attrs) { - // BaseInputConnection, IME_ACTION_GO, IME_FLAG_NO_EXTRACT_UI - API level 3 - attrs.actionLabel = null; - attrs.inputType = MainActivity.this.getKeyboardType(); - attrs.imeOptions = MainActivity.this.getKeyboardOptions(); - - kbText = new SpannableStringBuilder(MainActivity.this.keyboardText); - - InputConnection ic = new BaseInputConnection(this, true) { - boolean inited; - void updateText() { MainActivity.this.pushCmd(CMD_KEY_TEXT, kbText.toString()); } - - @Override - public Editable getEditable() { - if (!inited) { - // needed to set selection, otherwise random crashes later with backspacing - // set selection to end, so backspacing after opening keyboard with text still works - Selection.setSelection(kbText, kbText.toString().length()); - inited = true; - } - return kbText; - } - - @Override - public boolean setComposingText(CharSequence text, int newCursorPosition) { - boolean success = super.setComposingText(text, newCursorPosition); - updateText(); - return success; - } - - @Override - public boolean deleteSurroundingText(int beforeLength, int afterLength) { - - boolean success = super.deleteSurroundingText(beforeLength, afterLength); - updateText(); - return success; - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - boolean success = super.commitText(text, newCursorPosition); - updateText(); - return success; - } - - @Override - public boolean sendKeyEvent(KeyEvent ev) { - // getSelectionStart - API level 1 - if (ev.getAction() != KeyEvent.ACTION_DOWN) return super.sendKeyEvent(ev); - int code = ev.getKeyCode(); - int uni = ev.getUnicodeChar(); - - // start is -1 sometimes, and trying to insert/delete there crashes - int start = Selection.getSelectionStart(kbText); - if (start == -1) start = kbText.toString().length(); - - if (code == KeyEvent.KEYCODE_ENTER) { - // enter maps to \n but that should not be intercepted - } else if (code == KeyEvent.KEYCODE_DEL) { - if (start <= 0) return false; - kbText.delete(start - 1, start); - updateText(); - return false; - } else if (uni != 0) { - kbText.insert(start, String.valueOf((char)uni)); - updateText(); - return false; - } - return super.sendKeyEvent(ev); - } - - }; - //String text = MainActivity.this.keyboardText; - //if (text != null) ic.setComposingText(text, 0); - return ic; - } + setActiveView(view); } @@ -655,8 +589,9 @@ public class MainActivity extends Activity if (curView == null) return; // Try to avoid restarting input if possible - if (curView.kbText != null) { - String curText = curView.kbText.toString(); + CCView view = (CCView)curView; + if (view.kbText != null) { + String curText = view.kbText.toString(); if (text.equals(curText)) return; } @@ -668,9 +603,9 @@ public class MainActivity extends Activity input.restartInput(curView); } - public int getKeyboardType() { + public static int calcKeyboardType(int kbType) { // TYPE_CLASS_TEXT, TYPE_CLASS_NUMBER, TYPE_TEXT_VARIATION_PASSWORD - API level 3 - int type = keyboardType & 0xFF; + int type = kbType & 0xFF; if (type == 2) return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD; if (type == 1) return InputType.TYPE_CLASS_NUMBER; // KEYBOARD_TYPE_NUMERIC @@ -678,9 +613,9 @@ public class MainActivity extends Activity return InputType.TYPE_CLASS_TEXT; } - public int getKeyboardOptions() { + public static int calcKeyboardOptions(int kbType) { // IME_ACTION_GO, IME_FLAG_NO_EXTRACT_UI - API level 3 - if ((keyboardType & 0x100) != 0) { + if ((kbType & 0x100) != 0) { return EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NO_EXTRACT_UI; } else { return EditorInfo.IME_ACTION_GO | EditorInfo.IME_FLAG_NO_EXTRACT_UI; diff --git a/credits.txt b/credits.txt index 7f1fd4990..b564f03da 100644 --- a/credits.txt +++ b/credits.txt @@ -8,9 +8,9 @@ The OSX port would not have been possible without you, thanks! * Jerralish - reverse engineering and documenting the original classic map generation algorithm. * Cybertoon - Adding water animation, better metal step/dig sounds, identifying multiple flaws -* FabTheZen - suggestions about how to improve user experience, testing. * Cheesse - multiple suggestions, testing ClassicalSharp on AMD graphics cards. * Hemsindor - testing ClassicalSharp on OSX. +* headshotnoby - developing the Switch port And a big thanks to everyone else in the ClassiCube community (who I didn't mention here), who in the past have provided many suggestions and assisted in identifying bugs. diff --git a/doc/portability.md b/doc/portability.md index 1052f1e23..3478a0abd 100644 --- a/doc/portability.md +++ b/doc/portability.md @@ -3,9 +3,11 @@ Although most of the code is platform-independent, some per-platform functionali By default I try to automatically define appropriate backends for your system in `Core.h`. Define ```CC_BUILD_MANUAL``` to disable this. ## Before you start -* IEEE floating support is required. +* IEEE floating support is required. (Can be emulated in software, but will affect performance) * int must be 32-bits. 32-bit addressing (or more) is required. * Support for 8/16/32/64 integer types is required. (your compiler must support 64-bit arithmetic) +* At least around 2 MB of RAM is required at a minimum +* At least 128 kb for main thread stack size In other words, the codebase can theroetically be ported to any modern-ish hardware, but not stuff like a UNIVAC machine, the SuperFX chip on the SNES, or an 8-bit microcontroller. diff --git a/ios/CCIOS.xcodeproj/project.pbxproj b/ios/CCIOS.xcodeproj/project.pbxproj index acb7a1cda..ffa9fcf4a 100644 --- a/ios/CCIOS.xcodeproj/project.pbxproj +++ b/ios/CCIOS.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 9A4D0C642BDD168800E1695D /* TouchUI.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A4D0C632BDD168800E1695D /* TouchUI.c */; }; + 9A57ECEE2BCD1408006A89F0 /* AudioBackend.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A57ECED2BCD1408006A89F0 /* AudioBackend.c */; }; + 9A57ECF02BCD1413006A89F0 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A57ECEF2BCD1412006A89F0 /* main.c */; }; 9A62ADF5286D906F00E5E3DE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9A62ADF4286D906F00E5E3DE /* Assets.xcassets */; }; 9A7401D92B737D5C0040E575 /* Commands.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A7401D82B737D5B0040E575 /* Commands.c */; }; 9A7401DB2B7384060040E575 /* SSL.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A7401DA2B7384060040E575 /* SSL.c */; }; @@ -39,7 +42,6 @@ 9A89D55627F802F600FF3F80 /* EnvRenderer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D47A27F802F500FF3F80 /* EnvRenderer.c */; }; 9A89D55827F802F600FF3F80 /* Graphics_GL1.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D47E27F802F500FF3F80 /* Graphics_GL1.c */; }; 9A89D55927F802F600FF3F80 /* interop_ios.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D47F27F802F600FF3F80 /* interop_ios.m */; }; - 9A89D55A27F802F600FF3F80 /* Program.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D48127F802F600FF3F80 /* Program.c */; }; 9A89D55B27F802F600FF3F80 /* _type1.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D48227F802F600FF3F80 /* _type1.c */; }; 9A89D55C27F802F600FF3F80 /* Animations.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D48527F802F600FF3F80 /* Animations.c */; }; 9A89D55D27F802F600FF3F80 /* _psmodule.c in Sources */ = {isa = PBXBuildFile; fileRef = 9A89D48627F802F600FF3F80 /* _psmodule.c */; }; @@ -91,6 +93,9 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 9A4D0C632BDD168800E1695D /* TouchUI.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TouchUI.c; sourceTree = ""; }; + 9A57ECED2BCD1408006A89F0 /* AudioBackend.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = AudioBackend.c; sourceTree = ""; }; + 9A57ECEF2BCD1412006A89F0 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 9A62ADF4286D906F00E5E3DE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ClassiCube/Assets.xcassets; sourceTree = ""; }; 9A7401D82B737D5B0040E575 /* Commands.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Commands.c; sourceTree = ""; }; 9A7401DA2B7384060040E575 /* SSL.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SSL.c; sourceTree = ""; }; @@ -125,7 +130,6 @@ 9A89D47A27F802F500FF3F80 /* EnvRenderer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = EnvRenderer.c; sourceTree = ""; }; 9A89D47E27F802F500FF3F80 /* Graphics_GL1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Graphics_GL1.c; sourceTree = ""; }; 9A89D47F27F802F600FF3F80 /* interop_ios.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = interop_ios.m; sourceTree = ""; }; - 9A89D48127F802F600FF3F80 /* Program.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Program.c; sourceTree = ""; }; 9A89D48227F802F600FF3F80 /* _type1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = _type1.c; sourceTree = ""; }; 9A89D48527F802F600FF3F80 /* Animations.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Animations.c; sourceTree = ""; }; 9A89D48627F802F600FF3F80 /* _psmodule.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = _psmodule.c; sourceTree = ""; }; @@ -208,6 +212,9 @@ 9A89D37727F802F500FF3F80 /* src */ = { isa = PBXGroup; children = ( + 9A4D0C632BDD168800E1695D /* TouchUI.c */, + 9A57ECEF2BCD1412006A89F0 /* main.c */, + 9A57ECED2BCD1408006A89F0 /* AudioBackend.c */, 9A7401DA2B7384060040E575 /* SSL.c */, 9A7401D82B737D5B0040E575 /* Commands.c */, 9AC543412AE264B90086C85F /* EntityRenderers.c */, @@ -272,7 +279,6 @@ 9A89D4C927F802F600FF3F80 /* SelOutlineRenderer.c */, 9A89D4AA27F802F600FF3F80 /* Picking.c */, 9A89D39227F802F500FF3F80 /* Platform_Posix.c */, - 9A89D48127F802F600FF3F80 /* Program.c */, 9A89D4B327F802F600FF3F80 /* Protocol.c */, 9A89D4BE27F802F600FF3F80 /* Resources.c */, 9A89D4D127F802F600FF3F80 /* Screens.c */, @@ -402,6 +408,7 @@ 9A89D55F27F802F600FF3F80 /* _sfnt.c in Sources */, 9A89D55527F802F600FF3F80 /* HeldBlockRenderer.c in Sources */, 9A89D4FD27F802F600FF3F80 /* ExtMath.c in Sources */, + 9A57ECEE2BCD1408006A89F0 /* AudioBackend.c in Sources */, 9A89D56427F802F600FF3F80 /* String.c in Sources */, 9A89D57D27F802F600FF3F80 /* _smooth.c in Sources */, 9AC543402AE264AC0086C85F /* GameVersion.c in Sources */, @@ -416,7 +423,6 @@ 9A89D55827F802F600FF3F80 /* Graphics_GL1.c in Sources */, 9A89D59427F802F600FF3F80 /* Widgets.c in Sources */, 9A89D55927F802F600FF3F80 /* interop_ios.m in Sources */, - 9A89D55A27F802F600FF3F80 /* Program.c in Sources */, 9A7401DB2B7384060040E575 /* SSL.c in Sources */, 9A89D4F527F802F600FF3F80 /* _ftsynth.c in Sources */, 9A89D55D27F802F600FF3F80 /* _psmodule.c in Sources */, @@ -426,9 +432,11 @@ 9A89D56227F802F600FF3F80 /* _ftbase.c in Sources */, 9A89D56B27F802F600FF3F80 /* Server.c in Sources */, 9A89D50027F802F600FF3F80 /* _truetype.c in Sources */, + 9A4D0C642BDD168800E1695D /* TouchUI.c in Sources */, 9AC5433E2AE2649F0086C85F /* SystemFonts.c in Sources */, 9A89D57F27F802F600FF3F80 /* LWeb.c in Sources */, 9A89D56627F802F600FF3F80 /* Drawer2D.c in Sources */, + 9A57ECF02BCD1413006A89F0 /* main.c in Sources */, 9A89D57427F802F600FF3F80 /* MapRenderer.c in Sources */, 9A89D57627F802F600FF3F80 /* _pshinter.c in Sources */, 9A89D56A27F802F600FF3F80 /* Physics.c in Sources */, diff --git a/license.txt b/license.txt index e37e196ad..1a3f45e26 100644 --- a/license.txt +++ b/license.txt @@ -1,4 +1,4 @@ -Copyright (c) 2014 - 2022, UnknownShadow200 +Copyright (c) 2014 - 2024, UnknownShadow200 All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/misc/3ds/Makefile b/misc/3ds/Makefile index 901cd841a..f0b91f031 100644 --- a/misc/3ds/Makefile +++ b/misc/3ds/Makefile @@ -28,6 +28,10 @@ BUILD := build-3ds SOURCES := src misc/3ds third_party/bearssl/src INCLUDES := third_party/bearssl/inc +CIA_BANNER_BIN := $(TOPDIR)/misc/3ds/banner.bin +CIA_ICON_BIN := $(TOPDIR)/misc/3ds/icon.bin +CIA_SPEC_RSF := $(TOPDIR)/misc/3ds/spec.rsf + #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- @@ -44,7 +48,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -lcitro3d -lctru -lm +LIBS := -lctru -lm #--------------------------------------------------------------------------------- @@ -93,7 +97,7 @@ $(BUILD): #--------------------------------------------------------------------------------- clean: echo clean ... - rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf + rm -fr $(BUILD) $(TARGET).cia $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf #--------------------------------------------------------------------------------- else @@ -101,6 +105,14 @@ else #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- +$(OUTPUT).cia : $(OUTPUT).3dsx makerom + ./makerom -f cia -o "$(OUTPUT).cia" -elf "$(OUTPUT).elf" -rsf "$(CIA_SPEC_RSF)" -icon "$(CIA_ICON_BIN)" -banner "$(CIA_BANNER_BIN)" -exefslogo -target t + +makerom: + wget https://github.com/3DSGuy/Project_CTR/releases/download/makerom-v0.18.3/makerom-v0.18.3-ubuntu_x86_64.zip + unzip makerom-v0.18.3-ubuntu_x86_64.zip + chmod +x makerom + $(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh $(OUTPUT).elf : $(OFILES) @@ -121,4 +133,4 @@ $(OUTPUT).elf : $(OFILES) #--------------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------------- \ No newline at end of file +#--------------------------------------------------------------------------------------- diff --git a/misc/3ds/audio.wav b/misc/3ds/audio.wav new file mode 100644 index 000000000..02f3d15da Binary files /dev/null and b/misc/3ds/audio.wav differ diff --git a/misc/3ds/banner.bin b/misc/3ds/banner.bin new file mode 100644 index 000000000..d93f93064 Binary files /dev/null and b/misc/3ds/banner.bin differ diff --git a/misc/3ds/icon.bin b/misc/3ds/icon.bin new file mode 100644 index 000000000..09941bcec Binary files /dev/null and b/misc/3ds/icon.bin differ diff --git a/misc/3ds/readme.md b/misc/3ds/readme.md new file mode 100644 index 000000000..85bc067e0 --- /dev/null +++ b/misc/3ds/readme.md @@ -0,0 +1,5 @@ +Commands used to generate the .bin files: + +`bannertool makebanner -i banner.png -a audio.wav -o banner.bin` + +`bannertool makesmdh -s ClassiCube -l ClassiCube -p UnknownShadow200 -i icon.png -o icon.bin` \ No newline at end of file diff --git a/misc/3ds/spec.rsf b/misc/3ds/spec.rsf index ca39a9a8f..dfd7c4953 100644 --- a/misc/3ds/spec.rsf +++ b/misc/3ds/spec.rsf @@ -148,7 +148,6 @@ AccessControlInfo: - dlp:SRVR - dsp::DSP - frd:u - #- http:C - fs:USER - gsp::Gpu - hid:USER @@ -159,7 +158,6 @@ AccessControlInfo: - ptm:u - pxi:dev - soc:U - #- ssl:C - gsp::Lcd - y2r:u - ldr:ro @@ -203,5 +201,4 @@ SystemControlInfo: ptm: 0x0004013000002202L ro: 0x0004013000003702L socket: 0x0004013000002e02L - spi: 0x0004013000002302L - #ssl: 0x0004013000002f02 \ No newline at end of file + spi: 0x0004013000002302L \ No newline at end of file diff --git a/misc/ClassicalSharp/ClassicalSharp.zip b/misc/ClassicalSharp/ClassicalSharp.zip deleted file mode 100644 index 8353d19cf..000000000 Binary files a/misc/ClassicalSharp/ClassicalSharp.zip and /dev/null differ diff --git a/misc/ClassicalSharp/known_bugs.txt b/misc/ClassicalSharp/known_bugs.txt deleted file mode 100644 index a7990fc01..000000000 --- a/misc/ClassicalSharp/known_bugs.txt +++ /dev/null @@ -1,21 +0,0 @@ -* Blocks over 256 are not saved or loaded at all. -* Custom block information for blocks over 256 is not saved or loaded at all. -* /hold 0 prevents you deleting blocks until you change to another. -* Sometimes when holding air and your own model is a block model, you crash. -* Labels and buttons overlap in launcher with some fonts. (e.g. Lucida Console) -* terrain.png with width under 16 insta-crash the game -* catbox.moe texture packs/terrain.png links insta-crash the game -* Sometimes you randomly crash reading leveldatachunk packet on OSX -* Models with size of over 2 are not supported at all -* Direct3D9 backend uses an ill-formed vertex format that works by accident -* Alt text doesn't update its Y position if you click on chat -* Menu inputs (save, edit hotkey, water level, etc) are reset on window resize -* Chat input caret is reset on window resize -* Position in chat (if you scrolled up into history) is reset on window resize -* Two blank lines get shown in chat when you type /client cuboid -* Alt text is closed on window resize -* Changing server texture packs sometimes still retains textures from previous one -* Crashes at startup when another process has exclusively acquired Direct3D9 device -* Can't bind controls to mouse buttons -* Does not work at all on 64 bit macOS -* Making a gas block undeletable doesn't prevent placing blocks over it \ No newline at end of file diff --git a/misc/ClassicalSharp/readme.txt b/misc/ClassicalSharp/readme.txt deleted file mode 100644 index 507d0d061..000000000 --- a/misc/ClassicalSharp/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -Here lies ClassicalSharp, the original C# client. (works with Mono and .NET framework 2.0) -It has unfixed bugs and missing features. There's no reason to use it anymore. - -For licensing, please see license.txt inside ClassicalSharp.zip. -Absolutely no support or assistance will be provided for ClassicalSharp. \ No newline at end of file diff --git a/misc/ds/Makefile b/misc/ds/Makefile new file mode 100644 index 000000000..57c887ada --- /dev/null +++ b/misc/ds/Makefile @@ -0,0 +1,164 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# SPDX-FileContributor: Antonio Niño Díaz, 2023 +export BLOCKSDS ?= /opt/blocksds/core +export BLOCKSDSEXT ?= /opt/blocksds/external + +NAME := ClassiCube +GAME_TITLE := ClassiCube +GAME_SUBTITLE := Built with BlocksDS +GAME_AUTHOR := UnknownShadow200 +GAME_ICON := misc/ds/icon.bmp + +SOURCEDIRS := src +INCLUDEDIRS := +DEFINES := -DPLAT_NDS +LIBS := -ldswifi9 -lnds9 -lc +LIBDIRS := $(BLOCKSDS)/libs/dswifi $(BLOCKSDS)/libs/libnds + + +export WONDERFUL_TOOLCHAIN ?= /opt/wonderful +ARM_NONE_EABI_PATH ?= $(WONDERFUL_TOOLCHAIN)/toolchain/gcc-arm-none-eabi/bin/ + +# DLDI and internal SD slot of DSi +# -------------------------------- + +# Root folder of the SD image +SDROOT := sdroot +# Name of the generated image it "DSi-1.sd" for no$gba in DSi mode +SDIMAGE := image.bin + +# Build artifacts +# --------------- + +BUILDDIR := build-nds +ELF := build-nds/$(NAME).elf +DUMP := build-nds/$(NAME).dump +MAP := build-nds/$(NAME).map +ROM := $(NAME).nds + +# Tools +# ----- + +PREFIX := $(ARM_NONE_EABI_PATH)arm-none-eabi- +CC := $(PREFIX)gcc +CXX := $(PREFIX)g++ +LD := $(PREFIX)gcc +OBJDUMP := $(PREFIX)objdump +MKDIR := mkdir +RM := rm -rf + +# Verbose flag +# ------------ + +ifeq ($(VERBOSE),1) +V := +else +V := @ +endif + +# Source files +# ------------ + +SOURCES_S := $(foreach dir,$(SOURCEDIRS),$(wildcard $(dir)/*.s)) +SOURCES_C := $(foreach dir,$(SOURCEDIRS),$(wildcard $(dir)/*.c)) + +# Compiler and linker flags +# ------------------------- + +ARCH := -mthumb -mcpu=arm946e-s+nofp + +SPECS := $(BLOCKSDS)/sys/crts/ds_arm9.specs + +WARNFLAGS := -Wall + +INCLUDEFLAGS := $(foreach path,$(INCLUDEDIRS),-I$(path)) \ + $(foreach path,$(LIBDIRS),-I$(path)/include) + +LIBDIRSFLAGS := $(foreach path,$(LIBDIRS),-L$(path)/lib) + +ASFLAGS += -x assembler-with-cpp $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -ffunction-sections -fdata-sections \ + -specs=$(SPECS) + +CFLAGS += -std=gnu17 $(WARNFLAGS) $(DEFINES) $(INCLUDEFLAGS) \ + $(ARCH) -O2 -ffunction-sections -fdata-sections \ + -specs=$(SPECS) + +LDFLAGS := $(ARCH) $(LIBDIRSFLAGS) -Wl,-Map,$(MAP) $(DEFINES) \ + -Wl,--start-group $(LIBS) -Wl,--end-group -specs=$(SPECS) + +# Intermediate build files +# ------------------------ +OBJS := $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_S))) \ + $(addsuffix .o,$(addprefix $(BUILDDIR)/,$(SOURCES_C))) + +DEPS := $(OBJS:.o=.d) + +# Targets +# ------- + +.PHONY: all clean dump dldipatch sdimage + +all: $(ROM) + +# Combine the title strings +ifeq ($(strip $(GAME_SUBTITLE)),) + GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_AUTHOR) +else + GAME_FULL_TITLE := $(GAME_TITLE);$(GAME_SUBTITLE);$(GAME_AUTHOR) +endif + +$(ROM): $(ELF) + @echo " NDSTOOL $@" + $(V)$(BLOCKSDS)/tools/ndstool/ndstool -c $@ \ + -7 $(BLOCKSDS)/sys/default_arm7/arm7.elf -9 $(ELF) \ + -b $(GAME_ICON) "$(GAME_FULL_TITLE)" \ + $(NDSTOOL_ARGS) + +$(ELF): $(OBJS) + @echo " LD $@" + $(V)$(LD) -o $@ $(OBJS) $(LDFLAGS) + +$(DUMP): $(ELF) + @echo " OBJDUMP $@" + $(V)$(OBJDUMP) -h -C -S $< > $@ + +dump: $(DUMP) + +clean: + @echo " CLEAN" + $(V)$(RM) $(ROM) $(DUMP) build $(SDIMAGE) + +sdimage: + @echo " MKFATIMG $(SDIMAGE) $(SDROOT)" + $(V)$(BLOCKSDS)/tools/mkfatimg/mkfatimg -t $(SDROOT) $(SDIMAGE) + +dldipatch: $(ROM) + @echo " DLDIPATCH $(ROM)" + $(V)$(BLOCKSDS)/tools/dldipatch/dldipatch patch \ + $(BLOCKSDS)/sys/dldi_r4/r4tf.dldi $(ROM) + +# Rules +# ----- + +$(BUILDDIR)/%.s.o : %.s + @echo " AS $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(ASFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.c.o : %.c + @echo " CC $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< + +$(BUILDDIR)/%.arm.c.o : %.arm.c + @echo " CC $<" + @$(MKDIR) -p $(@D) + $(V)$(CC) $(CFLAGS) -MMD -MP -marm -mlong-calls -c -o $@ $< + +# Include dependency files if they exist +# -------------------------------------- + +-include $(DEPS) + diff --git a/misc/ds/icon.bmp b/misc/ds/icon.bmp new file mode 100644 index 000000000..c5a571c41 Binary files /dev/null and b/misc/ds/icon.bmp differ diff --git a/misc/gc/Makefile b/misc/gc/Makefile index a1db5bb8a..990d30ad6 100644 --- a/misc/gc/Makefile +++ b/misc/gc/Makefile @@ -33,7 +33,7 @@ LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lbba -lfat -logc -lm +LIBS := -lasnd -lbba -lfat -logc -lm #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -100,4 +100,4 @@ $(OUTPUT).elf: $(OFILES) #--------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------- \ No newline at end of file +#--------------------------------------------------------------------------------- diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.desktop b/misc/linux/flatpak/net.classicube.flatpak.client.desktop new file mode 100644 index 000000000..11f416da3 --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=ClassiCube +Exec=ClassiCubeLauncher +Comment=Sandbox building-block game +Type=Application +Icon=net.classicube.flatpak.client +Categories=Game;ActionGame; +Terminal=false +MimeType=x-scheme-handler/mc; +StartupWMClass=net.classicube.flatpak.client \ No newline at end of file diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml b/misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml new file mode 100644 index 000000000..7810cb367 --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml @@ -0,0 +1,133 @@ + + + net.classicube.flatpak.client + ClassiCube + Sandbox building-block game + + The ClassiCube Project + + CC0-1.0 + BSD-3-Clause + +

ClassiCube brings you back to the days of 2009 where one block game ruled them all, it includes such features as:

+
    +
  • Loads of blocks and items to choose from
  • +
  • Chatting with other players
  • +
  • An extremely simple network protocol to tinker with
  • +
  • Hundreds of creative and inventive worlds to explore online
  • +
  • A growing community
  • +
  • Hundreds of hours of entertainment
  • +
+
+ + + Classic mode features faithful classic gameplay + https://github.com/ClassiCube/ClassiCube/assets/6509348/eedee53f-f53e-456f-b51c-92c62079eee0 + + + Enhanced mode allows hacks like flying and noclipping, it also allows servers to provide many custom features + https://github.com/ClassiCube/ClassiCube/assets/6509348/b2fe0e2b-5d76-41ab-909f-048d0ad15f37 + + + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.6 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.5 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.4 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.3 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.2 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.1 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.0 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.9 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.8 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.7 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.6 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.5 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.4 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.3 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.2 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.1 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.0 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.9 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.8 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.7 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.6 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.5 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.4 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.3 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.2 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.1 + + + https://www.classicube.net/ + https://github.com/ClassiCube/ClassiCube/issues + https://www.patreon.com/ClassiCube + https://github.com/ClassiCube/ClassiCube + + Game + AdventureGame + ActionGame + + + pointing + keyboard + + + moderate + intense + + net.classicube.flatpak.client.desktop + + ClassiCube + +
diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.svg b/misc/linux/flatpak/net.classicube.flatpak.client.svg new file mode 100644 index 000000000..44324156b --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.svg @@ -0,0 +1,44 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.yml b/misc/linux/flatpak/net.classicube.flatpak.client.yml new file mode 100644 index 000000000..760e8b9d2 --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.yml @@ -0,0 +1,29 @@ +id: net.classicube.flatpak.client +runtime: org.freedesktop.Platform +runtime-version: '23.08' +sdk: org.freedesktop.Sdk +command: ClassiCubeLauncher +finish-args: + - --socket=x11 + - --device=dri + - --share=network + - --share=ipc + - --socket=pulseaudio +modules: + - name: ClassiCube + buildsystem: simple + build-commands: + - gcc -fno-math-errno src/*.c -o src/ClassiCube -O1 -DCC_BUILD_FLATPAK -DCC_BUILD_GLMODERN -rdynamic -lm -lpthread -lX11 -lXi -lGL -ldl + - install -Dm755 src/ClassiCube -t ${FLATPAK_DEST}/bin + - install -Dm755 ClassiCubeLauncher -t ${FLATPAK_DEST}/bin + - install -Dm644 misc/linux/flatpak/net.classicube.flatpak.client.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/net.classicube.flatpak.client.svg + - install -Dm644 misc/linux/flatpak/net.classicube.flatpak.client.desktop ${FLATPAK_DEST}/share/applications/net.classicube.flatpak.client.desktop + - install -Dm644 misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml ${FLATPAK_DEST}/share/metainfo/net.classicube.flatpak.client.metainfo.xml + sources: + - type: dir + path: ../../../ + - type: script + dest-filename: ClassiCubeLauncher + commands: + - mkdir -p ${XDG_DATA_HOME}/ClassiCube + - cd ${XDG_DATA_HOME}/ClassiCube && exec /app/bin/ClassiCube "$@" diff --git a/misc/macOS/mac_icon_gen.cs b/misc/macOS/mac_icon_gen.cs index 305ca9294..2fd317422 100644 --- a/misc/macOS/mac_icon_gen.cs +++ b/misc/macOS/mac_icon_gen.cs @@ -15,7 +15,11 @@ namespace test for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { Color c = bmp.GetPixel(x, y); - int p = (c.B << 24) | (c.G << 16) | (c.R << 8) | c.A; + + int r = c.R * c.A / 255; + int g = c.G * c.A / 255; + int b = c.B * c.A / 255; + int p = (r << 24) | (g << 16) | (b << 8) | c.A; sw.Write("0x" + ((uint)p).ToString("X8") + ","); } sw.WriteLine(); @@ -26,7 +30,7 @@ namespace test public static void Main(string[] args) { using (StreamWriter sw = new StreamWriter(dst)) { - sw.WriteLine("/* Generated using misc/mac_icon_gen.cs */"); + sw.WriteLine("/* Generated using misc/macOS/mac_icon_gen.cs */"); sw.WriteLine(""); sw.WriteLine("static const unsigned int CCIcon_Data[] = {"); DumpIcon(sw, 64, 64); diff --git a/misc/os2/Makefile b/misc/os2/Makefile new file mode 100644 index 000000000..265e98a44 --- /dev/null +++ b/misc/os2/Makefile @@ -0,0 +1,58 @@ +#-------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# +# NO_ICON: if set to anything, do not use icon. +# NO_NACP: if set to anything, no .nacp file is generated. +# APP_TITLE is the name of the app stored in the .nacp file (Optional) +# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) +# APP_VERSION is the version of the app stored in the .nacp file (Optional) +# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) +# ICON is the filename of the icon (.jpg), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .jpg +# - icon.jpg +# - /default_icon.jpg +# +# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .json +# - config.json +# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead +# of a homebrew executable (.nro). This is intended to be used for sysmodules. +# NACP building is skipped as well. +#--------------------------------------------------------------------------------- +TARGET := ClassiCube +BUILD_DIR := build +SOURCE_DIR:= src +DATA := data +C_SOURCES := $(wildcard $(SOURCE_DIR)/*.c) +C_OBJECTS := $(patsubst $(SOURCE_DIR)/%.c, $(BUILD_DIR)/%.o, $(C_SOURCES)) + +OBJECTS := $(C_OBJECTS) $(BUILD_DIR)/$(TARGET).res misc/os2/classicube.def +ENAME = ClassiCube +DEL = rm -f +APP_TITLE := ClassiCube +APP_AUTHOR := UnknownShadow200 + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CC := gcc +CFLAGS := -pipe -fno-math-errno -O3 -g -mtune=pentium4 -msse2 -march=i686 -idirafter /@unixroot/usr/include/os2tk45 -DOS2 +LDFLAGS := -Zhigh-mem -Zomf -Zargs-wild -Zargs-resp -Zlinker DISABLE -Zlinker 1121 +LIBS := -lcx -lmmpm2 -lpthread -lSDL2 + +$(TARGET).exe: $(BUILD_DIR) $(OBJECTS) + $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +$(C_OBJECTS): $(BUILD_DIR)/%.o : $(SOURCE_DIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ +$(BUILD_DIR)/$(TARGET).res: misc/os2/$(TARGET).rc misc/os2/$(TARGET).ico + wrc -r misc/os2/$(TARGET).rc -fo=$@ + diff --git a/misc/os2/classicube.ICO b/misc/os2/classicube.ICO new file mode 100644 index 000000000..04b06fca3 Binary files /dev/null and b/misc/os2/classicube.ICO differ diff --git a/misc/ps1/CMakeLists.txt b/misc/ps1/CMakeLists.txt new file mode 100644 index 000000000..1f78b62a4 --- /dev/null +++ b/misc/ps1/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.21) + +project( + ClassiCube + LANGUAGES C ASM + VERSION 1.0.0 + DESCRIPTION "ClassiCube PS1 port" + HOMEPAGE_URL "https://classicube.net" +) + +add_definitions(-DPLAT_PS1) +file(GLOB _sources ../../src/*.c) + +psn00bsdk_add_executable(template GPREL ${_sources}) + +psn00bsdk_add_cd_image( + iso # Target name + template # Output file name (= template.bin + template.cue) + iso.xml # Path to config file + DEPENDS template system.cnf +) diff --git a/misc/ps1/CMakePresets.json b/misc/ps1/CMakePresets.json new file mode 100644 index 000000000..97d842820 --- /dev/null +++ b/misc/ps1/CMakePresets.json @@ -0,0 +1,26 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 21, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset to build the project using PSn00bSDK.", + "generator": "Ninja", + "toolchainFile": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "PSN00BSDK_TC": "", + "PSN00BSDK_TARGET": "mipsel-none-elf" + }, + "warnings": { + "dev": false + } + } + ] +} diff --git a/misc/ps1/classicube.zip b/misc/ps1/classicube.zip new file mode 100644 index 000000000..80618ecac Binary files /dev/null and b/misc/ps1/classicube.zip differ diff --git a/misc/ps1/classicubezip.h b/misc/ps1/classicubezip.h new file mode 100644 index 000000000..4324b7ee7 --- /dev/null +++ b/misc/ps1/classicubezip.h @@ -0,0 +1,2142 @@ +const char ccTextures[23513] = { + 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0xac, 0x8a, 0x4d, 0xe1, 0xa5, 0x46, 0x24, 0x30, 0x04, 0x00, 0x00, + 0x30, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x63, 0x68, 0x61, + 0x72, 0x2e, 0x70, 0x6e, 0x67, 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, + 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x20, 0x08, 0x06, 0x00, 0x00, + 0x00, 0xa2, 0x9d, 0x7e, 0x84, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, + 0x4d, 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00, + 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0e, 0xbc, + 0x00, 0x00, 0x0e, 0xbc, 0x01, 0x95, 0xbc, 0x72, 0x49, 0x00, 0x00, + 0x00, 0x1a, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, + 0x61, 0x72, 0x65, 0x00, 0x50, 0x61, 0x69, 0x6e, 0x74, 0x2e, 0x4e, + 0x45, 0x54, 0x20, 0x76, 0x33, 0x2e, 0x35, 0x2e, 0x31, 0x31, 0x47, + 0xf3, 0x42, 0x37, 0x00, 0x00, 0x03, 0xac, 0x49, 0x44, 0x41, 0x54, + 0x68, 0x43, 0xd5, 0x96, 0xb1, 0x8a, 0x14, 0x41, 0x10, 0x86, 0xef, + 0x65, 0xe4, 0x04, 0x59, 0x84, 0x4b, 0x16, 0x14, 0x51, 0x10, 0x91, + 0x33, 0x30, 0x31, 0x32, 0xd0, 0xcc, 0xe8, 0x30, 0x31, 0xbc, 0x48, + 0x39, 0x10, 0xc4, 0x4c, 0xf0, 0x0d, 0x4c, 0x0c, 0x0f, 0x4c, 0x4d, + 0x2e, 0xf2, 0x01, 0x8e, 0xcb, 0x8c, 0x04, 0x41, 0x5f, 0x62, 0xdc, + 0x6f, 0x98, 0x7f, 0xfd, 0xa7, 0xa6, 0x7a, 0x67, 0x9a, 0x59, 0x1c, + 0x6c, 0xf8, 0x98, 0xea, 0xea, 0xea, 0x9a, 0xfa, 0x6b, 0x7a, 0x66, + 0xf7, 0x60, 0x33, 0x9a, 0x5d, 0x3c, 0xbe, 0x7b, 0x6b, 0xcb, 0x83, + 0xf5, 0x51, 0x6f, 0x0e, 0xdf, 0x3f, 0xbd, 0x6b, 0x2e, 0x3f, 0x9c, + 0x36, 0xdf, 0xde, 0x9c, 0xf4, 0xc0, 0xc7, 0x5a, 0x96, 0xb3, 0x92, + 0xb9, 0x23, 0xcb, 0xf9, 0x97, 0x93, 0xd3, 0xb3, 0x46, 0xe0, 0x88, + 0x73, 0x17, 0x7b, 0x7b, 0x75, 0xa3, 0x37, 0x07, 0x44, 0x46, 0xf1, + 0x82, 0xb5, 0x98, 0xcf, 0x6d, 0xcd, 0xb3, 0xab, 0x31, 0x77, 0x0c, + 0xf2, 0xf6, 0xee, 0x15, 0x6f, 0xd8, 0x5b, 0xdc, 0x5c, 0x11, 0xc9, + 0x93, 0x47, 0xfc, 0xea, 0xf0, 0x70, 0x70, 0x0a, 0xb2, 0xa7, 0x2f, + 0x58, 0x8b, 0xf9, 0xa6, 0x5e, 0x8d, 0xb9, 0x63, 0xf7, 0xbd, 0x76, + 0x2e, 0x6e, 0xae, 0x2e, 0x96, 0x06, 0x00, 0xcd, 0x50, 0x23, 0xf6, + 0xd5, 0x80, 0x68, 0x1b, 0x73, 0x47, 0x9b, 0x57, 0x30, 0x97, 0xaf, + 0xb5, 0xe3, 0x62, 0x9c, 0x23, 0xd2, 0x4f, 0x00, 0x78, 0x53, 0xa6, + 0x34, 0xc0, 0xf3, 0xb9, 0x1d, 0xe7, 0xee, 0x37, 0xe6, 0x8e, 0x34, + 0xbf, 0xd9, 0xbd, 0x9b, 0x0d, 0x70, 0xb1, 0x34, 0xa2, 0xf6, 0x04, + 0x64, 0x39, 0x2b, 0x99, 0x3b, 0xb2, 0x9c, 0x4e, 0xea, 0xdc, 0xe2, + 0x0d, 0x80, 0xf8, 0x21, 0xfc, 0xef, 0x7f, 0x05, 0x12, 0x47, 0x0f, + 0x17, 0x0b, 0xf1, 0x23, 0x88, 0x48, 0x35, 0xc1, 0x91, 0x3f, 0xcb, + 0x59, 0x49, 0x3b, 0x74, 0xbf, 0x68, 0x4f, 0x18, 0x9e, 0x6b, 0x88, + 0x92, 0x2d, 0xc5, 0xa6, 0x88, 0x49, 0xc3, 0xe3, 0x6b, 0xf7, 0xee, + 0x1c, 0x4a, 0xa6, 0x27, 0xeb, 0x4f, 0x58, 0xb6, 0xbf, 0xf3, 0x5c, + 0xb3, 0x18, 0xf7, 0xd5, 0xd0, 0x95, 0xb1, 0xdc, 0x50, 0x21, 0x7a, + 0xb7, 0xfd, 0x1d, 0xc7, 0xd6, 0x97, 0x1f, 0x24, 0x5e, 0x73, 0x6f, + 0x8c, 0xef, 0x03, 0x6f, 0xc8, 0xae, 0xe6, 0x74, 0x65, 0x2c, 0x37, + 0x5c, 0x24, 0xb6, 0x84, 0xb8, 0x5f, 0x1f, 0x35, 0x70, 0x9f, 0x6c, + 0x04, 0x12, 0xaf, 0x2b, 0x3e, 0xe5, 0x51, 0x2e, 0xd9, 0x6a, 0x86, + 0xae, 0x5d, 0x19, 0xcb, 0x0d, 0x89, 0xf8, 0xf9, 0xe5, 0x63, 0x8b, + 0xe6, 0x12, 0x81, 0x50, 0x15, 0xcf, 0x3a, 0x4d, 0xd0, 0x9c, 0x35, + 0x09, 0x16, 0x31, 0x0f, 0x42, 0x75, 0x25, 0x56, 0xc2, 0xb1, 0xa1, + 0x2b, 0x63, 0xb9, 0x41, 0x71, 0x14, 0xe2, 0x85, 0x33, 0x47, 0xa8, + 0x7c, 0x8c, 0xb7, 0x2f, 0xde, 0x6f, 0xe7, 0xd8, 0x0c, 0xcd, 0x75, + 0x32, 0x3c, 0x0f, 0x36, 0x3e, 0xa1, 0x79, 0xf4, 0x77, 0x65, 0x2c, + 0x37, 0x78, 0x22, 0xfe, 0xbb, 0x0d, 0x12, 0x51, 0x8b, 0xf6, 0x93, + 0x2b, 0x3b, 0x1d, 0x10, 0x1b, 0xd1, 0x95, 0xb1, 0xdc, 0xa8, 0x11, + 0x8c, 0x30, 0x3f, 0x19, 0x63, 0x94, 0x9a, 0x81, 0xad, 0xa6, 0x77, + 0x65, 0x2c, 0x3a, 0x28, 0x62, 0x0b, 0xc7, 0x5b, 0xbc, 0x7a, 0xfa, + 0xba, 0xb9, 0x76, 0x7d, 0xd5, 0x23, 0xc6, 0x47, 0xbe, 0x5e, 0x5c, + 0x34, 0x9f, 0xcf, 0xcf, 0x5b, 0x81, 0x0e, 0x3e, 0xd6, 0x94, 0xf7, + 0xd9, 0xf1, 0xcb, 0xe6, 0xd1, 0xbd, 0xe7, 0x2d, 0xd8, 0xf8, 0x58, + 0xcb, 0x72, 0x3a, 0xbf, 0x7f, 0xfd, 0xd8, 0x72, 0x79, 0x75, 0xb5, + 0x6d, 0xb0, 0xc8, 0xf6, 0x8c, 0xd0, 0x77, 0xcc, 0x6d, 0x00, 0x42, + 0x4b, 0x0d, 0x80, 0x7d, 0x35, 0x00, 0xf1, 0x34, 0x54, 0x27, 0x4d, + 0xf7, 0xc9, 0xf6, 0x8c, 0xd0, 0x77, 0xfc, 0x8b, 0x06, 0x28, 0xb7, + 0x1a, 0x20, 0xf1, 0x90, 0xe5, 0x74, 0x62, 0x03, 0xfc, 0x04, 0x74, + 0xaf, 0x54, 0x2d, 0x7d, 0x87, 0x0a, 0x51, 0x91, 0xb5, 0x0d, 0x98, + 0xf2, 0x0a, 0x28, 0xf7, 0x9c, 0x06, 0xa8, 0x09, 0x7e, 0x02, 0xb0, + 0xb3, 0x3d, 0x3b, 0x51, 0x31, 0x5e, 0x84, 0xf0, 0x22, 0xb3, 0x62, + 0xc1, 0x0b, 0x52, 0x51, 0xa0, 0x0f, 0x20, 0xb4, 0x76, 0xe7, 0x8f, + 0xf1, 0xda, 0x43, 0x73, 0x9c, 0x52, 0x6c, 0x2d, 0xa9, 0x68, 0xc7, + 0xc5, 0x44, 0xa6, 0x34, 0xc0, 0x8b, 0xf7, 0xa2, 0xb1, 0xf5, 0x6b, + 0x10, 0xfd, 0xfb, 0x14, 0x39, 0x96, 0x2f, 0x15, 0xed, 0xb8, 0x18, + 0x07, 0xa1, 0x53, 0x1a, 0xc0, 0x8d, 0xf5, 0x7e, 0x63, 0xc7, 0x02, + 0x22, 0xb5, 0xf1, 0x63, 0x8c, 0xe5, 0x43, 0x24, 0x75, 0x0e, 0x84, + 0x0b, 0x17, 0xeb, 0xc2, 0xc0, 0xbf, 0xd4, 0xc0, 0xdc, 0xbf, 0xd8, + 0xa0, 0x9b, 0x8b, 0xd2, 0x53, 0xc5, 0x0f, 0x59, 0x7c, 0xb6, 0xa7, + 0xe4, 0x17, 0x5a, 0x1f, 0xcb, 0x87, 0x48, 0xea, 0x1d, 0x08, 0x17, + 0x12, 0x5f, 0x22, 0x6b, 0x82, 0x37, 0x29, 0x3e, 0x01, 0xe1, 0xc5, + 0x0a, 0xad, 0xc5, 0xf8, 0x18, 0x27, 0x11, 0xa5, 0x3c, 0x42, 0x31, + 0x9e, 0xcf, 0xc5, 0x03, 0x22, 0xa9, 0x79, 0x20, 0x5c, 0xb8, 0x18, + 0x28, 0x89, 0xc7, 0x06, 0xfc, 0x1e, 0xaf, 0x9b, 0xc7, 0x22, 0x62, + 0x21, 0xf8, 0xbd, 0x58, 0x8f, 0xcf, 0xf6, 0x44, 0xbf, 0x93, 0xe5, + 0x89, 0x68, 0x2f, 0x22, 0xa9, 0x7f, 0x20, 0x5c, 0xb8, 0x18, 0x09, + 0xd7, 0xdc, 0x1b, 0x50, 0x6a, 0x82, 0x17, 0xe3, 0x45, 0xbb, 0x78, + 0x50, 0xe1, 0xa5, 0xf8, 0x6c, 0x0f, 0x6b, 0xd1, 0x27, 0x4a, 0xf9, + 0x62, 0x9e, 0x54, 0xb4, 0xa3, 0x27, 0xed, 0xe2, 0xdc, 0xe7, 0x0d, + 0x88, 0x4d, 0x00, 0xdd, 0xdc, 0x8b, 0x50, 0x21, 0x42, 0x3e, 0x28, + 0xc5, 0x43, 0x16, 0xcf, 0x4f, 0x68, 0x24, 0xfe, 0xfd, 0xcd, 0xd0, + 0xcf, 0x6f, 0x2a, 0xda, 0xc9, 0x9e, 0xbc, 0xf0, 0x46, 0xb8, 0x70, + 0x8f, 0xf1, 0x62, 0x01, 0x01, 0xfe, 0x04, 0x44, 0x26, 0x6e, 0x4a, + 0xbc, 0x7e, 0x4a, 0x33, 0x4a, 0x8d, 0xf0, 0x98, 0x54, 0xb4, 0x13, + 0xff, 0xe9, 0xd5, 0x92, 0x9d, 0x10, 0x35, 0x34, 0xc2, 0x5a, 0x6d, + 0xbc, 0x8b, 0x89, 0xf0, 0x84, 0xb3, 0x06, 0xe0, 0x57, 0x4c, 0x2a, + 0xda, 0xb9, 0x7f, 0xfc, 0xa4, 0xc9, 0x58, 0xdf, 0x79, 0xd8, 0xdc, + 0x3c, 0x5a, 0x0f, 0x04, 0xe3, 0x63, 0x4d, 0x71, 0x14, 0x1a, 0xc5, + 0xf8, 0x09, 0x71, 0x24, 0x4a, 0xf1, 0x12, 0x9a, 0xc5, 0x02, 0x6b, + 0x12, 0xe3, 0xa2, 0x24, 0x14, 0xbb, 0x74, 0x0a, 0xf0, 0x4f, 0x6a, + 0x00, 0x62, 0x22, 0x88, 0xcc, 0xc4, 0x0b, 0xad, 0x13, 0xab, 0x42, + 0xa3, 0x10, 0xe6, 0x88, 0xcd, 0x9a, 0x52, 0x13, 0x2f, 0x21, 0xba, + 0x0a, 0xcd, 0x69, 0x4c, 0x6c, 0xc2, 0xf4, 0x13, 0x70, 0xd0, 0xfc, + 0x01, 0xa7, 0xa1, 0x57, 0x13, 0xfa, 0xb5, 0xf2, 0x86, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, 0x50, + 0x4b, 0x03, 0x04, 0x14, 0x03, 0x00, 0x00, 0x08, 0x00, 0x3a, 0x92, + 0x8d, 0x58, 0xda, 0xcf, 0x04, 0x24, 0xb1, 0x56, 0x00, 0x00, 0x6c, + 0x57, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x74, 0x65, 0x72, 0x72, + 0x61, 0x69, 0x6e, 0x2e, 0x70, 0x6e, 0x67, 0x64, 0x5b, 0x05, 0x50, + 0x5b, 0x61, 0xb3, 0x4d, 0x08, 0xee, 0xee, 0x56, 0x0a, 0xc5, 0xa5, + 0x48, 0x71, 0x2f, 0x0e, 0x85, 0xe2, 0x2e, 0xc5, 0x29, 0xee, 0x4e, + 0xb0, 0x02, 0xa5, 0x48, 0x8b, 0x3b, 0x14, 0x77, 0x77, 0x87, 0xe2, + 0x5a, 0x9c, 0x22, 0xc1, 0xdd, 0x1d, 0x02, 0xe4, 0xe5, 0xff, 0x67, + 0xde, 0xcc, 0x9b, 0x79, 0x77, 0x32, 0xc9, 0xbd, 0x73, 0x33, 0x73, + 0xe7, 0xee, 0x7e, 0x7b, 0x76, 0xf7, 0x7c, 0x67, 0xa3, 0xd4, 0x3e, + 0xc9, 0x61, 0xa1, 0x93, 0xa3, 0x03, 0x00, 0x00, 0x2c, 0x05, 0xf9, + 0x8f, 0xea, 0x00, 0x00, 0x10, 0x00, 0x3f, 0xc0, 0xa8, 0xc8, 0xf0, + 0xef, 0x9d, 0xc6, 0x66, 0xf8, 0x0d, 0x74, 0x76, 0x5f, 0x4d, 0x5d, + 0x77, 0x75, 0x33, 0x2f, 0x5a, 0x67, 0x57, 0x27, 0x2b, 0x5b, 0x7b, + 0x4b, 0x5a, 0x77, 0x1f, 0x67, 0x4b, 0x5a, 0x4b, 0x6f, 0x5b, 0x2b, + 0x00, 0xc0, 0xfb, 0x5f, 0x61, 0x8a, 0x6d, 0x2c, 0x7d, 0x3b, 0x56, + 0x18, 0x6c, 0x9f, 0xd9, 0x1f, 0x91, 0xbd, 0x80, 0x89, 0x2a, 0x62, + 0x0e, 0xe1, 0x01, 0x59, 0xac, 0x4b, 0xdc, 0x52, 0x27, 0xae, 0x18, + 0x05, 0x67, 0x0a, 0xd9, 0x30, 0xfc, 0xac, 0x6c, 0x54, 0xa6, 0x60, + 0xd8, 0xf4, 0xe3, 0x4e, 0x3d, 0xec, 0x76, 0xea, 0xfa, 0x39, 0x54, + 0xe7, 0x2f, 0x7d, 0x5c, 0xc9, 0xed, 0x07, 0x7d, 0xa5, 0x0e, 0x8b, + 0x61, 0xa6, 0xc9, 0x94, 0xec, 0x2e, 0xe3, 0x7b, 0xbf, 0x0b, 0x1d, + 0x31, 0xc8, 0x79, 0x69, 0x72, 0x97, 0x89, 0x60, 0xcc, 0x3d, 0x0c, + 0xea, 0x1b, 0x18, 0x30, 0x21, 0xae, 0xea, 0x5f, 0x9d, 0x34, 0x36, + 0xb5, 0x0d, 0xc9, 0x59, 0x86, 0xe9, 0xca, 0x8b, 0x79, 0xed, 0x3c, + 0xf8, 0x6c, 0x41, 0xbd, 0x0f, 0x83, 0xd8, 0x34, 0xbb, 0xc4, 0x8e, + 0xb0, 0x7a, 0xee, 0xd9, 0x72, 0xc2, 0x07, 0xbd, 0x72, 0x47, 0x37, + 0xbb, 0x23, 0x57, 0x65, 0x9b, 0xe9, 0x61, 0x8f, 0xa7, 0x39, 0x14, + 0x53, 0xd6, 0x92, 0xbb, 0xe8, 0xc7, 0x0c, 0xa2, 0x8d, 0x89, 0x46, + 0xc2, 0x41, 0x9e, 0xf9, 0x73, 0x50, 0xc6, 0xf9, 0xc7, 0x0e, 0x98, + 0x1b, 0x03, 0x17, 0x0c, 0x0a, 0x5d, 0x7b, 0xa8, 0xbe, 0xe5, 0xb8, + 0x58, 0x88, 0x2a, 0xf1, 0xd4, 0x80, 0x59, 0xdc, 0xac, 0xeb, 0xb3, + 0x10, 0x91, 0x3f, 0xde, 0x13, 0x38, 0x6d, 0xdd, 0x6a, 0x5c, 0x42, + 0x62, 0x17, 0xdb, 0x2f, 0x44, 0x25, 0x7f, 0x06, 0x12, 0x43, 0x17, + 0xd7, 0x83, 0xb2, 0x1f, 0x6e, 0x2b, 0x5f, 0x45, 0xd6, 0x67, 0x69, + 0xaa, 0xef, 0xaa, 0xb2, 0x73, 0xf8, 0xed, 0x5e, 0x4f, 0x37, 0x4c, + 0xdc, 0x77, 0x82, 0x6c, 0x87, 0xaf, 0x75, 0xa8, 0x7c, 0x9a, 0x6f, + 0x0d, 0x82, 0x77, 0xa1, 0x06, 0x8f, 0x3e, 0x99, 0xe4, 0x30, 0xc5, + 0x0b, 0x1f, 0x61, 0x67, 0x6d, 0x71, 0xeb, 0x6e, 0x9d, 0x48, 0x25, + 0x50, 0xea, 0x69, 0x68, 0xc5, 0x39, 0xfb, 0xde, 0x94, 0x47, 0x8f, + 0xc6, 0xd1, 0x4a, 0xc5, 0xe8, 0xaf, 0x7b, 0xa3, 0xc7, 0xc9, 0xfd, + 0x7e, 0xf1, 0x00, 0x48, 0xcc, 0xaf, 0x4f, 0x74, 0x4b, 0xfb, 0x01, + 0x3d, 0xee, 0xf1, 0xe7, 0xe3, 0x82, 0x59, 0x86, 0xde, 0x90, 0x29, + 0x68, 0x59, 0xeb, 0x6e, 0x5a, 0x8c, 0xea, 0x48, 0xf6, 0xad, 0xe8, + 0x02, 0xcd, 0xa4, 0x57, 0x20, 0xc7, 0xfa, 0x7e, 0xc0, 0xa2, 0x7d, + 0x21, 0x21, 0x33, 0xc3, 0x39, 0xdd, 0x6a, 0x8d, 0x60, 0x53, 0xd0, + 0xee, 0x2f, 0x71, 0xb3, 0x53, 0xac, 0x0e, 0xb4, 0xd5, 0xd9, 0x3d, + 0x72, 0x1f, 0x5d, 0x95, 0xab, 0x57, 0x18, 0x64, 0x2a, 0xa8, 0x7d, + 0x23, 0xd3, 0x47, 0x29, 0xd1, 0x49, 0x6f, 0xb7, 0xfc, 0x65, 0x47, + 0xcb, 0xd1, 0xe0, 0xcc, 0x8a, 0xa7, 0x66, 0x0b, 0x26, 0x38, 0xd5, + 0xc1, 0xae, 0x97, 0xf3, 0x7b, 0x7d, 0x66, 0x17, 0x12, 0x7f, 0x03, + 0x55, 0x32, 0xf1, 0xd6, 0x6d, 0xab, 0xf7, 0xc2, 0x98, 0x90, 0xc0, + 0x0b, 0x6b, 0x3f, 0x1e, 0xa5, 0x91, 0x58, 0x68, 0x2e, 0x85, 0xdc, + 0x36, 0xf3, 0x06, 0xb6, 0xbf, 0x0c, 0xf0, 0xbf, 0xe7, 0xc4, 0x90, + 0xf7, 0x39, 0xce, 0xe0, 0xe5, 0xd1, 0x99, 0xd3, 0xb5, 0xb3, 0x15, + 0x6c, 0xac, 0x28, 0x8c, 0x7d, 0x56, 0x2b, 0xfe, 0x3d, 0x50, 0xe2, + 0x75, 0xe4, 0xde, 0xff, 0xfd, 0x46, 0x44, 0x91, 0xcb, 0x52, 0xab, + 0xa1, 0x69, 0xa6, 0x2c, 0x49, 0x66, 0x4d, 0xf5, 0xe9, 0x3b, 0x2c, + 0x84, 0xbb, 0x53, 0x06, 0xf6, 0x13, 0x7a, 0x3d, 0x84, 0x7c, 0x84, + 0x11, 0xd1, 0x8e, 0xe4, 0xfa, 0x58, 0xb0, 0x7e, 0x65, 0xad, 0xbb, + 0x1e, 0x88, 0x46, 0x96, 0xe2, 0xf5, 0x14, 0xd8, 0xb1, 0xa4, 0xff, + 0x4a, 0xae, 0x4a, 0x0c, 0x60, 0xbd, 0x65, 0x7d, 0xc9, 0xb8, 0x52, + 0xe9, 0xae, 0x0f, 0x76, 0x4d, 0x60, 0x3d, 0x9d, 0x78, 0xfa, 0xfd, + 0xc6, 0x04, 0xb9, 0x25, 0x2d, 0xe1, 0xde, 0xab, 0x6a, 0xcc, 0x49, + 0xf6, 0x9a, 0xfd, 0x52, 0x87, 0x64, 0xdd, 0x77, 0xae, 0x3f, 0xce, + 0xca, 0x8d, 0x3e, 0x89, 0xce, 0x2d, 0xf6, 0xfd, 0xd2, 0x23, 0x47, + 0x7e, 0xd3, 0xfb, 0xe2, 0xaa, 0xd9, 0x14, 0x85, 0x93, 0x4a, 0xeb, + 0xc2, 0x1a, 0xee, 0xb1, 0x96, 0x3f, 0xc4, 0xdc, 0x29, 0x04, 0xb5, + 0x64, 0x79, 0x11, 0x12, 0x8c, 0x2c, 0x0b, 0xa6, 0xf5, 0x66, 0x6f, + 0x9b, 0xb0, 0x4f, 0xfb, 0xe2, 0xc6, 0xab, 0x0e, 0x34, 0xa6, 0xbe, + 0x2a, 0xd1, 0x98, 0x3b, 0x7c, 0x5d, 0xe7, 0xc8, 0x9a, 0xae, 0x3f, + 0xfd, 0x7a, 0x9c, 0x6f, 0x5b, 0x49, 0x46, 0xad, 0x67, 0x5e, 0xaa, + 0x36, 0x6a, 0x36, 0x2a, 0xe2, 0xad, 0x6f, 0x99, 0x38, 0xe7, 0x4f, + 0x38, 0xb3, 0x8c, 0x69, 0xda, 0x28, 0x36, 0xea, 0xee, 0xb0, 0x52, + 0xf7, 0x09, 0xe9, 0xd6, 0x43, 0xe0, 0xed, 0xb2, 0x35, 0xa1, 0x63, + 0xd0, 0x1e, 0x8b, 0x26, 0x42, 0x2a, 0xd2, 0x6f, 0x7c, 0x0b, 0xcd, + 0x09, 0xeb, 0xa9, 0xbf, 0x5b, 0xee, 0x44, 0x9b, 0x8e, 0x69, 0x32, + 0x3a, 0x29, 0x7c, 0x6e, 0x79, 0x67, 0x7e, 0x94, 0x57, 0xa3, 0xc2, + 0x1d, 0xec, 0x48, 0x60, 0x37, 0x55, 0xce, 0xb2, 0xd3, 0xe5, 0xba, + 0xc3, 0xcb, 0xa5, 0xda, 0x0a, 0xf6, 0xad, 0x89, 0x19, 0xaf, 0xaf, + 0x5f, 0xe3, 0x28, 0x66, 0x7d, 0x39, 0x8a, 0x84, 0x27, 0xd1, 0x2a, + 0x09, 0x94, 0xdd, 0x9d, 0x78, 0xe8, 0x27, 0xff, 0x1d, 0xbb, 0xd6, + 0x2f, 0x5f, 0x16, 0x36, 0xb1, 0x11, 0x11, 0x93, 0xf3, 0xc9, 0xb4, + 0x54, 0x36, 0xa4, 0x64, 0xe6, 0xe1, 0x21, 0x66, 0xf2, 0x4d, 0x1c, + 0xf0, 0xc4, 0x58, 0x79, 0x48, 0x90, 0xe5, 0x0d, 0x34, 0x3b, 0x12, + 0xcc, 0xb7, 0xf0, 0x61, 0x5e, 0x77, 0x2e, 0x96, 0xd2, 0xb9, 0x0b, + 0x3d, 0x37, 0x3c, 0x0f, 0x7f, 0x52, 0xb2, 0xbf, 0x49, 0x9a, 0x1f, + 0x5d, 0x8e, 0xd8, 0xe5, 0xcd, 0xb0, 0x8e, 0x78, 0x66, 0xfc, 0x25, + 0xa9, 0xc4, 0x2b, 0x89, 0xfc, 0xdd, 0xb3, 0x2a, 0xf8, 0x49, 0x93, + 0xdf, 0x69, 0x66, 0x6a, 0x55, 0x5e, 0x12, 0x23, 0xbe, 0xce, 0xca, + 0x3c, 0x2f, 0x75, 0x9e, 0x07, 0x8b, 0xdc, 0x72, 0x9f, 0xb4, 0x30, + 0x63, 0x95, 0x83, 0x07, 0xd5, 0xd4, 0xd4, 0x30, 0xb2, 0x56, 0x5b, + 0xbc, 0xb2, 0x0e, 0xe8, 0xc0, 0x68, 0x71, 0x72, 0xd2, 0xd9, 0x12, + 0xc6, 0x8b, 0x44, 0xa0, 0xc3, 0x8f, 0xbc, 0xd2, 0x54, 0x9c, 0x79, + 0x34, 0x8e, 0xb5, 0x16, 0xcc, 0x2f, 0xc7, 0xf9, 0xe2, 0x5c, 0x55, + 0xe5, 0xc1, 0x7b, 0x3c, 0xc6, 0xa9, 0xd1, 0x5e, 0x99, 0xbc, 0xca, + 0x21, 0x14, 0x73, 0xb5, 0x60, 0x46, 0x47, 0x6d, 0x19, 0x9a, 0xae, + 0xd5, 0x6a, 0x4a, 0xb2, 0xb2, 0xff, 0x37, 0xa3, 0xe9, 0x74, 0xd5, + 0x22, 0x7d, 0xaf, 0xf6, 0x13, 0x05, 0x75, 0x55, 0x96, 0x89, 0x11, + 0xc2, 0x1e, 0xd5, 0xdf, 0x99, 0x74, 0xa2, 0x90, 0x6a, 0x77, 0xf1, + 0x3f, 0x1e, 0xbc, 0x0f, 0xba, 0x65, 0x37, 0x81, 0x9a, 0xbf, 0x84, + 0x1d, 0x98, 0xee, 0x5a, 0xb8, 0x81, 0x0e, 0x77, 0xb5, 0xc8, 0x09, + 0xce, 0xab, 0x2f, 0x99, 0xd1, 0x57, 0x44, 0x18, 0x99, 0xee, 0x4b, + 0x75, 0xda, 0x28, 0x23, 0x59, 0x56, 0x97, 0x18, 0xa6, 0xb1, 0xec, + 0xc0, 0x9b, 0x9b, 0xbb, 0xf6, 0x8c, 0x79, 0x2a, 0x61, 0x5e, 0x71, + 0x06, 0xdf, 0x45, 0x10, 0xa9, 0x2c, 0xab, 0xc6, 0x2e, 0xa2, 0x03, + 0x3d, 0x61, 0x34, 0x9a, 0xce, 0x0f, 0x6d, 0x46, 0xe8, 0x97, 0xf2, + 0x13, 0x06, 0x3f, 0x61, 0xf3, 0x86, 0xf7, 0x9e, 0x5e, 0xe7, 0xbc, + 0x5a, 0x5b, 0x85, 0x9e, 0x95, 0x80, 0x07, 0xe0, 0xa7, 0x37, 0x64, + 0x73, 0x97, 0x87, 0x66, 0x8c, 0x23, 0x3f, 0x7c, 0x30, 0x1a, 0x06, + 0xb3, 0x23, 0x8c, 0x7e, 0xcf, 0x05, 0xac, 0x68, 0xab, 0x76, 0x09, + 0xed, 0xca, 0x57, 0x76, 0x79, 0x17, 0xae, 0x06, 0xd6, 0xb5, 0xd4, + 0xea, 0x45, 0x49, 0x2d, 0x8a, 0xa4, 0xad, 0x8c, 0x74, 0x09, 0xae, + 0x50, 0xbc, 0x1f, 0xb6, 0xfa, 0x4c, 0xfb, 0x2e, 0xef, 0x53, 0xad, + 0xb9, 0x37, 0xe8, 0x5d, 0x91, 0x6a, 0x89, 0xc3, 0xa7, 0xe6, 0x54, + 0xbf, 0x81, 0x5a, 0x86, 0xd1, 0xc9, 0xdf, 0x4a, 0xac, 0xf4, 0x86, + 0xd4, 0x10, 0x86, 0x3d, 0x7a, 0xeb, 0x8c, 0x62, 0x36, 0x56, 0x83, + 0x9e, 0xe5, 0x7f, 0xba, 0x68, 0x26, 0x15, 0xed, 0x04, 0xa1, 0xdf, + 0x56, 0x22, 0x85, 0x28, 0x1e, 0x3f, 0xc2, 0xd7, 0xab, 0x92, 0x96, + 0x9a, 0xf3, 0x51, 0xd8, 0x8c, 0xca, 0xdb, 0x84, 0x61, 0xd0, 0x08, + 0xb8, 0xba, 0x13, 0x69, 0x1b, 0x2f, 0x65, 0xe4, 0xa8, 0x02, 0x23, + 0x5e, 0x20, 0xe9, 0x2c, 0x8b, 0xab, 0x01, 0xb1, 0x7c, 0x66, 0x1c, + 0x2a, 0xe0, 0x51, 0x53, 0x88, 0x75, 0xde, 0x93, 0xa5, 0xb2, 0xe4, + 0x8e, 0x6f, 0x6e, 0x3e, 0x5e, 0x2f, 0x6e, 0x27, 0xe1, 0x24, 0x29, + 0x1c, 0x5c, 0xac, 0x50, 0x80, 0x19, 0x4f, 0x7a, 0x30, 0x2f, 0x34, + 0x4c, 0x13, 0xfd, 0x39, 0xda, 0x73, 0x08, 0x24, 0x22, 0x60, 0xb3, + 0x64, 0x1f, 0x20, 0x85, 0xe4, 0x73, 0x2e, 0xe2, 0xa5, 0xe4, 0xf5, + 0xaa, 0xbf, 0x99, 0xce, 0x58, 0x2d, 0x7b, 0x7b, 0x6e, 0x4f, 0xf0, + 0x59, 0x6e, 0x28, 0x89, 0x4b, 0xef, 0x2f, 0xf1, 0xf6, 0x11, 0xc9, + 0x52, 0x7e, 0x88, 0xf0, 0xcf, 0xbf, 0xc4, 0xc0, 0x5b, 0x4f, 0x49, + 0xd6, 0x02, 0xdc, 0x47, 0xf9, 0xe5, 0xd2, 0xc9, 0x31, 0xab, 0xed, + 0xe1, 0x7c, 0x17, 0x17, 0x44, 0x56, 0xb6, 0x3b, 0x92, 0xde, 0x8a, + 0x05, 0xa3, 0x2b, 0xb6, 0xb1, 0xf7, 0x76, 0xf6, 0x4f, 0x37, 0x79, + 0x37, 0x7b, 0x98, 0xb2, 0x7b, 0x5c, 0x52, 0x49, 0x99, 0xf6, 0x9c, + 0xef, 0xb0, 0xa6, 0xdf, 0x7b, 0x48, 0x17, 0xf7, 0x33, 0xdd, 0x06, + 0xab, 0x2e, 0x0b, 0x2a, 0x87, 0x39, 0xd2, 0x6a, 0xce, 0x90, 0x64, + 0x22, 0x44, 0x85, 0x7d, 0xa8, 0x41, 0xa7, 0x26, 0xf9, 0x32, 0x95, + 0xda, 0xfb, 0xdd, 0x45, 0xcb, 0xcf, 0x53, 0x16, 0x84, 0x4e, 0x28, + 0xbf, 0xdf, 0x60, 0xa9, 0x8c, 0xa0, 0x93, 0x73, 0xd5, 0xb3, 0x42, + 0xa6, 0x76, 0xba, 0x8d, 0x46, 0x1e, 0x32, 0xb7, 0x55, 0xe5, 0x25, + 0xaa, 0xea, 0x95, 0x17, 0xde, 0x78, 0x56, 0xbb, 0x12, 0xd2, 0xd4, + 0x78, 0xa3, 0x94, 0xdc, 0x83, 0xc5, 0x1e, 0x32, 0xbe, 0x70, 0x54, + 0x55, 0xdf, 0x90, 0x60, 0x47, 0x73, 0xf9, 0x71, 0xbf, 0x0a, 0xd3, + 0xc3, 0xaa, 0x2a, 0x25, 0xb4, 0x3a, 0x74, 0x1a, 0x9d, 0xfa, 0x63, + 0xdd, 0x0f, 0x11, 0xd3, 0x19, 0x61, 0x54, 0x4b, 0xf0, 0x07, 0x2b, + 0x7d, 0x9c, 0x6c, 0xa0, 0x8c, 0x1e, 0x41, 0x20, 0x97, 0x78, 0xa2, + 0x75, 0x26, 0x9a, 0x26, 0x02, 0xe5, 0x67, 0x7c, 0x08, 0xb7, 0x2d, + 0x25, 0xd7, 0x7c, 0xb6, 0x19, 0xc8, 0xfd, 0xbe, 0xc0, 0xb1, 0xb5, + 0xf0, 0x13, 0x5f, 0x6e, 0xd4, 0x7c, 0x18, 0x6d, 0x0d, 0x95, 0x08, + 0x84, 0x75, 0x9f, 0xb1, 0xf6, 0xb8, 0x44, 0x8d, 0xf0, 0x4a, 0xf1, + 0xe8, 0xf4, 0x52, 0xf3, 0x10, 0x5f, 0xd9, 0xf8, 0x6d, 0x9c, 0x98, + 0x30, 0xfd, 0x1f, 0xcc, 0x23, 0x63, 0xdc, 0xe4, 0xe0, 0xce, 0xd8, + 0x75, 0x2a, 0xf6, 0x76, 0xd3, 0x1f, 0x9b, 0xf2, 0x8a, 0x28, 0xa4, + 0xc5, 0xb8, 0xce, 0x5f, 0xb3, 0x6d, 0xf0, 0xe6, 0x42, 0x7f, 0x7e, + 0x55, 0xc5, 0xb2, 0xd1, 0xf9, 0xa1, 0x2c, 0x77, 0xf6, 0x97, 0x94, + 0xe7, 0x1e, 0x4b, 0x47, 0x03, 0x2f, 0x27, 0xa2, 0xeb, 0x94, 0x95, + 0xd1, 0xec, 0x86, 0x47, 0xb2, 0x8e, 0x44, 0x9d, 0x75, 0x91, 0x78, + 0x61, 0xe4, 0xb4, 0x02, 0x33, 0xa1, 0xd9, 0xd6, 0x56, 0xb4, 0x47, + 0x8e, 0x4c, 0x66, 0xce, 0x6c, 0x8b, 0xfd, 0x9a, 0x9c, 0x56, 0xe3, + 0xb7, 0x64, 0x59, 0x2c, 0xa9, 0x14, 0xeb, 0xc5, 0x36, 0x62, 0x64, + 0xc4, 0xe0, 0x2d, 0xc1, 0x3b, 0x56, 0xf7, 0x2f, 0xe3, 0xb7, 0xca, + 0x0b, 0x6e, 0x3b, 0x54, 0x63, 0x4c, 0xeb, 0x34, 0x61, 0x93, 0x33, + 0x54, 0x56, 0xe2, 0x87, 0xdf, 0xa3, 0x10, 0xf3, 0x19, 0x65, 0x5b, + 0xd6, 0xde, 0x31, 0xdd, 0x6d, 0x21, 0x61, 0xd3, 0x53, 0x23, 0x1e, + 0x1c, 0x81, 0xda, 0x51, 0xae, 0xaa, 0x62, 0x6c, 0x0a, 0xd0, 0x0b, + 0xa5, 0x9c, 0x25, 0xce, 0xf4, 0x68, 0xf1, 0x4d, 0x97, 0x64, 0xd4, + 0xa5, 0x26, 0x24, 0x69, 0xd4, 0x45, 0x42, 0xcc, 0xfe, 0xd9, 0xf0, + 0x25, 0xcc, 0x53, 0x4e, 0x74, 0x53, 0x22, 0xa1, 0xc6, 0x13, 0x11, + 0x12, 0x26, 0x7c, 0x54, 0x1c, 0x96, 0x68, 0x5f, 0x18, 0xbc, 0xc4, + 0x70, 0xf6, 0xfc, 0x8d, 0x0e, 0x94, 0x1c, 0xcd, 0xc6, 0x5a, 0x3f, + 0x89, 0x6e, 0x7c, 0xb2, 0x2f, 0x57, 0x96, 0x0d, 0x34, 0x21, 0x0a, + 0xa9, 0x70, 0x74, 0xb7, 0x08, 0x37, 0x20, 0x29, 0x92, 0xc2, 0xdd, + 0x56, 0x2c, 0x95, 0xd0, 0xae, 0xab, 0xaf, 0xc7, 0x26, 0xf6, 0xb6, + 0x99, 0x94, 0x20, 0x3f, 0x96, 0x12, 0xa2, 0x1b, 0xcb, 0xdb, 0x4f, + 0xf3, 0x17, 0x1a, 0xda, 0xb4, 0x31, 0x54, 0xbe, 0x5f, 0xaa, 0x47, + 0x5e, 0x85, 0xe4, 0x12, 0x14, 0x50, 0xcb, 0x0e, 0x89, 0x5a, 0x68, + 0x9d, 0x00, 0x4e, 0xac, 0x19, 0x08, 0x1f, 0x44, 0x9d, 0x69, 0x13, + 0x65, 0x71, 0xf2, 0x70, 0x3e, 0x7d, 0xbe, 0x1f, 0x2b, 0x1a, 0xc7, + 0xa9, 0x8f, 0x16, 0x91, 0xa6, 0x57, 0x51, 0x35, 0x77, 0xce, 0x65, + 0x2d, 0x31, 0xef, 0xeb, 0x46, 0xbf, 0x69, 0xab, 0xbd, 0xef, 0xfa, + 0x96, 0xd2, 0xc9, 0x1a, 0x5c, 0xf2, 0xd9, 0xe4, 0xeb, 0xce, 0xde, + 0x8e, 0xaf, 0x83, 0x64, 0x28, 0x68, 0x34, 0xe9, 0x10, 0xc9, 0xca, + 0x0e, 0x51, 0x71, 0x6e, 0x31, 0x4f, 0x8b, 0xf7, 0xd7, 0xdf, 0xb7, + 0x9c, 0x89, 0xd5, 0xe9, 0x93, 0x63, 0x2c, 0xe3, 0xcc, 0x75, 0x71, + 0x6f, 0x25, 0xcf, 0x69, 0x86, 0x1a, 0x6a, 0x10, 0xe5, 0xcd, 0xbe, + 0x90, 0x8b, 0xd1, 0x3b, 0x3c, 0x61, 0x07, 0x23, 0x98, 0xd8, 0xf7, + 0xf5, 0xd5, 0xe6, 0x16, 0x8f, 0x41, 0x1d, 0x8e, 0x8e, 0xc6, 0x19, + 0x42, 0x85, 0x3a, 0x7f, 0x2a, 0x82, 0x96, 0x04, 0x90, 0xf8, 0xf3, + 0x53, 0xe2, 0x26, 0xcd, 0x74, 0x9f, 0x3c, 0x03, 0x34, 0x51, 0x5e, + 0xe2, 0xdf, 0x66, 0xac, 0x97, 0x6c, 0xac, 0x6f, 0xbc, 0xc5, 0x1b, + 0xc7, 0xe2, 0xd0, 0x74, 0x5f, 0x8a, 0xc0, 0xde, 0xf8, 0x66, 0xba, + 0xef, 0x13, 0xff, 0xea, 0x29, 0x21, 0xa4, 0x4a, 0x93, 0x68, 0x2b, + 0xa2, 0x8b, 0x31, 0x4a, 0x17, 0xf1, 0xae, 0x7f, 0x35, 0x1c, 0x93, + 0xbc, 0x7e, 0x98, 0xbb, 0xd2, 0x75, 0x9f, 0x01, 0x7a, 0x8c, 0xcc, + 0xee, 0x0c, 0xa6, 0xfd, 0xf5, 0xc4, 0x94, 0x85, 0x84, 0x9e, 0x95, + 0xa3, 0xcc, 0x76, 0xb2, 0xbb, 0x5f, 0x36, 0x1e, 0x3a, 0xbe, 0x21, + 0x7e, 0x11, 0x63, 0xe8, 0x7e, 0xd8, 0xdf, 0xa6, 0x66, 0x6e, 0x87, + 0xcd, 0xe9, 0xf3, 0x6f, 0xf8, 0xdf, 0x67, 0xe9, 0x8b, 0x7f, 0x82, + 0xa7, 0x2c, 0x6f, 0x40, 0xb2, 0x5f, 0x39, 0x82, 0xe4, 0xb8, 0x06, + 0x85, 0x70, 0xd1, 0x2e, 0xcc, 0xb9, 0x76, 0x2e, 0xcc, 0x89, 0x7a, + 0x1d, 0x89, 0x19, 0x1f, 0x0b, 0xd0, 0xe2, 0xed, 0x64, 0xaa, 0xd0, + 0x1b, 0x65, 0x12, 0x3e, 0x7d, 0x22, 0x28, 0x47, 0x61, 0xfc, 0xb6, + 0xad, 0x73, 0xf0, 0x39, 0x4d, 0xe0, 0x20, 0x96, 0xed, 0x33, 0x1f, + 0x86, 0x2c, 0xa7, 0x87, 0x0f, 0x80, 0x98, 0x38, 0xdb, 0xa5, 0x00, + 0x2f, 0x70, 0xe4, 0xab, 0xf1, 0xb2, 0xfe, 0x74, 0x03, 0xc5, 0x1b, + 0xae, 0x8c, 0xd4, 0xee, 0x83, 0x01, 0xd3, 0x62, 0x8f, 0x7d, 0x41, + 0xbd, 0xc4, 0xb1, 0x02, 0xf7, 0x24, 0xd7, 0xb0, 0xe1, 0x0a, 0x67, + 0xf5, 0xc0, 0xaa, 0xe1, 0x64, 0xac, 0x86, 0x62, 0xcb, 0xa5, 0x1c, + 0xe8, 0x5f, 0x9c, 0x44, 0x7a, 0xd5, 0x54, 0x9e, 0xfd, 0xde, 0xe7, + 0x25, 0xb2, 0xe6, 0x32, 0x0a, 0xe9, 0xcf, 0x66, 0x3c, 0x4b, 0x14, + 0xfc, 0xf1, 0xc2, 0xaa, 0xba, 0x14, 0x25, 0x4e, 0xce, 0xe8, 0xbf, + 0x69, 0xf6, 0x42, 0x1e, 0x96, 0x4a, 0x96, 0xb4, 0xc9, 0x3e, 0x2b, + 0x5b, 0x64, 0x18, 0x5c, 0xb4, 0xac, 0x09, 0x06, 0xd5, 0xe0, 0x5b, + 0xa0, 0x91, 0x0f, 0x86, 0x98, 0x4a, 0xa8, 0x14, 0x87, 0x09, 0xa8, + 0x17, 0xfc, 0x71, 0x99, 0x46, 0xa7, 0xa2, 0x67, 0xe8, 0x2c, 0xfe, + 0xb9, 0xdc, 0x28, 0x90, 0xf6, 0xf2, 0x70, 0xcb, 0x9f, 0xc2, 0xf8, + 0x37, 0x90, 0xa5, 0x3e, 0x0d, 0x43, 0xa8, 0xb0, 0xe8, 0x00, 0x25, + 0x53, 0x86, 0x08, 0x73, 0xb5, 0xff, 0x6a, 0xf7, 0x20, 0x0a, 0xcd, + 0x92, 0x0b, 0x48, 0x2a, 0x2b, 0x90, 0x2f, 0xba, 0x98, 0xf5, 0xbb, + 0x22, 0x50, 0xcf, 0x95, 0x83, 0xec, 0xc4, 0x8d, 0xa7, 0x74, 0x8e, + 0x0a, 0xa8, 0x0a, 0xd2, 0x3a, 0x8b, 0x69, 0x52, 0xb7, 0xb1, 0x29, + 0xe5, 0xae, 0x06, 0xc9, 0xec, 0x7e, 0xef, 0x69, 0x99, 0x56, 0x93, + 0x9b, 0xf7, 0x65, 0x1c, 0x05, 0x31, 0xdc, 0x7f, 0x4d, 0x84, 0x98, + 0x2d, 0xb9, 0xea, 0x8a, 0xa9, 0x99, 0x90, 0xff, 0xf6, 0xce, 0xb1, + 0xcc, 0xf8, 0xe3, 0x13, 0x19, 0xa6, 0x51, 0x82, 0xd6, 0x36, 0xd1, + 0xb3, 0x97, 0xf1, 0xde, 0x70, 0xdc, 0x59, 0x77, 0x82, 0xf0, 0x87, + 0x10, 0xfd, 0x37, 0x4f, 0xe5, 0x48, 0x74, 0x52, 0xe2, 0xd1, 0x9a, + 0xad, 0xa2, 0x43, 0x67, 0x1a, 0x4a, 0x4e, 0x5e, 0x06, 0xed, 0x21, + 0xc0, 0x69, 0xa8, 0xd7, 0x3e, 0x59, 0x1e, 0x1d, 0x84, 0x70, 0xff, + 0x37, 0x6e, 0x22, 0xf3, 0x60, 0x98, 0xa1, 0x81, 0x0e, 0xd3, 0xcd, + 0x21, 0x4a, 0xad, 0xde, 0x07, 0x49, 0xea, 0xaa, 0x88, 0xcc, 0x53, + 0xe2, 0x0c, 0x1f, 0x30, 0xc2, 0x82, 0x2e, 0x97, 0xad, 0xe8, 0x47, + 0x32, 0x15, 0xeb, 0xf8, 0xe3, 0x2c, 0xe1, 0xe1, 0x84, 0xc7, 0xe7, + 0x93, 0x0b, 0x2e, 0x01, 0xb2, 0xfd, 0x68, 0x32, 0x3e, 0xa1, 0x7a, + 0x19, 0x9f, 0x2b, 0x09, 0x15, 0x13, 0x0a, 0x5f, 0x1b, 0x0e, 0xd9, + 0x6f, 0xda, 0x26, 0x24, 0xab, 0x04, 0xf3, 0x0c, 0x8c, 0xcf, 0x34, + 0x9e, 0xc2, 0x46, 0xef, 0xf0, 0xee, 0x7e, 0xbb, 0xd0, 0xf0, 0x3c, + 0xf7, 0x4d, 0xfb, 0x7c, 0xf3, 0xa5, 0x95, 0xf7, 0x5c, 0x75, 0x09, + 0xf6, 0xa6, 0x1f, 0xf8, 0xc8, 0xdf, 0xed, 0x8f, 0xee, 0x66, 0x9e, + 0xff, 0x26, 0xe1, 0xfd, 0x05, 0xef, 0x09, 0xd7, 0xf6, 0x76, 0xf7, + 0xdd, 0x18, 0x88, 0x70, 0x0a, 0x0b, 0xe5, 0xea, 0xc9, 0x71, 0x2e, + 0xe0, 0xa3, 0x6f, 0x8f, 0x58, 0x19, 0xca, 0xb4, 0x01, 0x5e, 0xe6, + 0x96, 0x58, 0x08, 0x9b, 0xbf, 0x8c, 0x34, 0xef, 0xd3, 0xee, 0x14, + 0x3a, 0xda, 0x48, 0xdf, 0x5a, 0x5f, 0xeb, 0xdd, 0xc0, 0xe7, 0x71, + 0x56, 0xa8, 0x45, 0xc2, 0xd7, 0x3a, 0x74, 0xdf, 0xdc, 0x61, 0xbc, + 0x9f, 0xba, 0x30, 0xf1, 0xb1, 0x38, 0xd5, 0x89, 0x97, 0xa9, 0x70, + 0x49, 0xc9, 0x6a, 0xc2, 0x74, 0x94, 0x05, 0xce, 0x22, 0x3f, 0xcb, + 0xdb, 0xfa, 0xb3, 0xeb, 0xa1, 0xbe, 0x47, 0xa7, 0xaa, 0x97, 0x15, + 0xa5, 0x1c, 0x3f, 0x87, 0x2f, 0x6a, 0x53, 0xd5, 0x98, 0x27, 0x76, + 0xc4, 0xf4, 0x61, 0x2d, 0x13, 0x0d, 0x9c, 0xb6, 0x17, 0x25, 0x82, + 0xd9, 0x41, 0xa0, 0x3e, 0x45, 0xc2, 0x90, 0xfd, 0x63, 0xbd, 0x08, + 0x78, 0xfd, 0x88, 0xe8, 0x26, 0xa5, 0xa0, 0x89, 0x0a, 0x3f, 0xfc, + 0x51, 0x2d, 0x22, 0x01, 0x00, 0x5a, 0x80, 0xc2, 0x47, 0x49, 0x4d, + 0xef, 0xcc, 0x93, 0x4e, 0x9f, 0x44, 0xca, 0xa1, 0x80, 0xfd, 0xc7, + 0x91, 0xb6, 0xba, 0xaa, 0xaa, 0xa3, 0x32, 0xb1, 0x69, 0x92, 0x4f, + 0x08, 0xb4, 0x92, 0xf8, 0x00, 0x34, 0x5c, 0x7a, 0x19, 0x05, 0xc2, + 0x81, 0x37, 0xb6, 0x46, 0xc9, 0x85, 0x79, 0xf6, 0xb6, 0x1e, 0xfa, + 0x86, 0x4a, 0x57, 0x92, 0xdd, 0x3e, 0xd4, 0x4a, 0x37, 0x14, 0xd5, + 0x9d, 0x4d, 0x39, 0x72, 0x3c, 0xd4, 0x3e, 0x9d, 0x5f, 0xcf, 0xf5, + 0x03, 0xb7, 0xcd, 0x77, 0x1a, 0xed, 0x6d, 0x2b, 0xa8, 0xfc, 0xe8, + 0xf5, 0xe6, 0x64, 0xd4, 0xe9, 0x65, 0xd6, 0x10, 0x7b, 0x40, 0xb4, + 0x2e, 0x7c, 0x55, 0x5d, 0xe7, 0x6d, 0x71, 0x15, 0xa3, 0xa3, 0xe3, + 0x2f, 0x6f, 0x59, 0xea, 0xe8, 0xca, 0xcb, 0x24, 0xdd, 0xf9, 0x19, + 0xf4, 0xd5, 0x61, 0xeb, 0xfd, 0x63, 0x3f, 0x7e, 0x44, 0x96, 0xc9, + 0x6a, 0x05, 0x04, 0x0d, 0xdd, 0xdf, 0x47, 0xa3, 0x9e, 0xc2, 0xbc, + 0x9f, 0x6f, 0x27, 0xfb, 0xbe, 0x24, 0x16, 0x3e, 0x32, 0xf1, 0x53, + 0xeb, 0xee, 0x85, 0x13, 0xbc, 0x60, 0x62, 0x6a, 0x01, 0x96, 0xb8, + 0x75, 0x37, 0xe1, 0xa7, 0x54, 0x53, 0x29, 0x7b, 0xf9, 0xf0, 0xeb, + 0x3f, 0x1e, 0x56, 0x28, 0x79, 0xa6, 0xf1, 0x6a, 0x4c, 0x91, 0x55, + 0x11, 0x80, 0x92, 0xf9, 0xa3, 0x92, 0xdf, 0x62, 0xd5, 0x8b, 0x2a, + 0x52, 0x79, 0xb4, 0x80, 0xdb, 0x36, 0x34, 0x92, 0xae, 0x29, 0xf5, + 0xf5, 0x2f, 0xb4, 0x87, 0xd6, 0x2d, 0x9c, 0xc3, 0xab, 0xdf, 0x99, + 0xd7, 0xdf, 0x3a, 0xb9, 0x98, 0xea, 0xa4, 0xfe, 0x6c, 0xc8, 0xd9, + 0xde, 0xfe, 0x66, 0x8d, 0xb9, 0x11, 0x5b, 0xde, 0xc5, 0xf7, 0x27, + 0xf6, 0x14, 0x3b, 0x85, 0xb1, 0xa1, 0xbc, 0x0b, 0xe1, 0xec, 0x25, + 0x20, 0x3c, 0x6b, 0xcb, 0xe9, 0x7d, 0x19, 0x20, 0xac, 0x0f, 0xaf, + 0x79, 0xed, 0xd4, 0x58, 0x71, 0xad, 0xe4, 0xd7, 0x08, 0xe1, 0xb2, + 0xef, 0xbe, 0x1c, 0xea, 0xb7, 0x90, 0xb0, 0x74, 0xd8, 0x50, 0xca, + 0x2f, 0xdf, 0xca, 0xb3, 0xd8, 0x4e, 0x6f, 0xb3, 0x90, 0xdd, 0xa4, + 0x1f, 0xde, 0x10, 0x35, 0xf8, 0x53, 0xe7, 0x9e, 0x0d, 0x3b, 0x72, + 0x72, 0x9e, 0x3a, 0x5e, 0xae, 0xeb, 0xa9, 0x1f, 0x31, 0x01, 0x18, + 0x03, 0x42, 0xcd, 0xb0, 0xc1, 0xf3, 0xba, 0x67, 0xd8, 0x2b, 0x99, + 0x3e, 0xae, 0xe2, 0x2d, 0xf2, 0xa5, 0x1a, 0x00, 0xfc, 0x91, 0xa9, + 0x47, 0x79, 0x92, 0xd6, 0x05, 0x86, 0x42, 0x06, 0x8b, 0x50, 0x45, + 0xc0, 0xc5, 0x78, 0x8a, 0x42, 0x7a, 0x39, 0xa9, 0x32, 0xb6, 0xe0, + 0xc2, 0x92, 0xa5, 0xe2, 0x50, 0xee, 0x95, 0xf8, 0x4e, 0x53, 0x7e, + 0x07, 0x9a, 0x62, 0xb3, 0x59, 0x13, 0xdb, 0x37, 0xf1, 0x59, 0xac, + 0xfc, 0xd4, 0xc6, 0x38, 0x79, 0x91, 0x4c, 0xd8, 0xa8, 0x1f, 0x0a, + 0x79, 0xde, 0xe6, 0xf4, 0xa4, 0xba, 0x57, 0x1c, 0xf3, 0x7b, 0xb5, + 0x7a, 0x82, 0xd5, 0x2b, 0x13, 0x94, 0x6b, 0x83, 0x7a, 0xaa, 0x8f, + 0x1f, 0x42, 0x25, 0xfb, 0x2e, 0x99, 0x64, 0x4b, 0x43, 0xfe, 0x2d, + 0x72, 0xe4, 0x79, 0x67, 0x4e, 0x3d, 0xcf, 0x2c, 0x71, 0xda, 0xef, + 0xbc, 0x80, 0x30, 0xa2, 0x3f, 0x37, 0x61, 0x6a, 0xaf, 0xa2, 0xe0, + 0x2f, 0xb1, 0x1c, 0x90, 0x10, 0xcb, 0xb4, 0x67, 0xfc, 0x8e, 0xdc, + 0xff, 0x2d, 0xc1, 0x38, 0xbc, 0x26, 0xce, 0x4d, 0xe8, 0x9e, 0x33, + 0xaf, 0x92, 0xe0, 0x79, 0x88, 0x1c, 0x88, 0x76, 0x05, 0x7f, 0xfd, + 0xe5, 0x1f, 0xbb, 0x4d, 0xbb, 0xa4, 0x1e, 0x93, 0x03, 0x7b, 0xc7, + 0xee, 0xa0, 0x56, 0x7b, 0xa5, 0xc3, 0x22, 0x44, 0x01, 0xb4, 0x42, + 0xc3, 0x49, 0x2a, 0x0d, 0x7e, 0x3b, 0x4e, 0x1f, 0x1c, 0x5c, 0xf4, + 0x63, 0xc0, 0x1f, 0xdf, 0x2f, 0xe3, 0x13, 0xe3, 0x04, 0x68, 0x11, + 0xc0, 0x12, 0x72, 0x64, 0x2d, 0x4b, 0xfb, 0x02, 0xc1, 0x2a, 0xbb, + 0x39, 0xae, 0x74, 0x13, 0x0b, 0x5d, 0x92, 0x29, 0xab, 0x7d, 0xe2, + 0xc8, 0xa1, 0xfc, 0x93, 0x25, 0x21, 0xd9, 0x1a, 0x34, 0xbb, 0x7a, + 0x47, 0x75, 0x3e, 0x4c, 0xcb, 0xc0, 0xfd, 0xe3, 0xd1, 0xc5, 0x0e, + 0x67, 0x5b, 0x7c, 0x71, 0xb5, 0x0f, 0xec, 0x65, 0x56, 0xa1, 0x76, + 0x8d, 0x8a, 0x23, 0xf6, 0xd3, 0x04, 0x98, 0x5c, 0xf8, 0x38, 0x5e, + 0xfe, 0x0b, 0x80, 0x58, 0x24, 0x03, 0x5f, 0xfd, 0x51, 0x3b, 0x66, + 0xd6, 0x2a, 0x6b, 0x92, 0x37, 0xd6, 0xc0, 0x63, 0x9b, 0xee, 0x8b, + 0x7f, 0xf7, 0x6e, 0xdb, 0xcd, 0x62, 0x2a, 0x83, 0xfe, 0x48, 0xff, + 0xca, 0xe5, 0x35, 0x71, 0xea, 0xbe, 0x0e, 0xa1, 0xce, 0xdb, 0x25, + 0x29, 0x57, 0xb5, 0x3c, 0xf5, 0xe8, 0xe8, 0xfe, 0x03, 0x72, 0x9c, + 0xc7, 0xa8, 0x0f, 0xcc, 0x3a, 0x92, 0x04, 0x5d, 0xf3, 0x37, 0x7c, + 0x46, 0x63, 0x9b, 0xc0, 0x40, 0xaa, 0xe1, 0xe5, 0x04, 0xb3, 0x55, + 0x0a, 0x37, 0x24, 0x06, 0xdb, 0x3d, 0x16, 0xd2, 0x91, 0x92, 0x96, + 0xd5, 0x58, 0xd6, 0x48, 0x6d, 0xab, 0x47, 0xea, 0x57, 0x9e, 0xe9, + 0x07, 0xa0, 0x05, 0x12, 0x9d, 0x5c, 0x19, 0xfb, 0xa2, 0xab, 0xbb, + 0x8b, 0x22, 0xa2, 0x3f, 0xa4, 0x5c, 0x11, 0x3b, 0xb5, 0x30, 0xec, + 0x85, 0xbc, 0x3e, 0xb4, 0x6c, 0xde, 0x61, 0xdd, 0x23, 0x91, 0x29, + 0x40, 0xd7, 0x5a, 0xeb, 0x1c, 0x9c, 0x9a, 0xbd, 0x8f, 0x14, 0x68, + 0xbc, 0xd6, 0x2d, 0x79, 0x7c, 0x4a, 0x91, 0x4e, 0x8a, 0xb3, 0x6f, + 0x6a, 0x1d, 0x8a, 0x01, 0xdd, 0x1d, 0x39, 0x1d, 0xf4, 0x84, 0x3d, + 0x3b, 0x0c, 0x31, 0x23, 0x4a, 0xe4, 0x2b, 0x9e, 0x42, 0x27, 0x48, + 0x5b, 0x9d, 0x9d, 0x73, 0x31, 0x65, 0x18, 0xae, 0x68, 0x8e, 0xb8, + 0x3d, 0x29, 0xa7, 0x77, 0x7e, 0x5a, 0xf4, 0x36, 0x0c, 0xf2, 0x58, + 0xbb, 0xd1, 0x1a, 0xc1, 0x34, 0x9f, 0x8d, 0x85, 0xa9, 0xe3, 0xda, + 0x16, 0x3a, 0xd5, 0x98, 0x3c, 0xb3, 0x20, 0x55, 0x02, 0x3b, 0xcf, + 0xf2, 0x02, 0xca, 0x22, 0xd4, 0xbb, 0x3f, 0xa3, 0x42, 0x0e, 0xae, + 0xa3, 0xa6, 0xac, 0xca, 0x4c, 0xa5, 0xf3, 0xb0, 0xbd, 0x3b, 0x51, + 0xf3, 0xcd, 0x5c, 0x9c, 0x84, 0x26, 0x2c, 0x26, 0x2d, 0xb8, 0x17, + 0x9f, 0x1c, 0xcf, 0x2e, 0xa5, 0xe9, 0xb0, 0xd9, 0x9a, 0x06, 0xab, + 0x1a, 0x03, 0xe0, 0x2f, 0x3c, 0xc1, 0x98, 0xcd, 0x59, 0x2b, 0x4a, + 0xbe, 0x73, 0x16, 0xb8, 0xe6, 0x00, 0x5b, 0x75, 0xa6, 0x04, 0x57, + 0x3f, 0x3f, 0x9a, 0x38, 0x0f, 0x95, 0x6f, 0xd7, 0xa0, 0xc6, 0x71, + 0x2e, 0x7a, 0xef, 0x37, 0x8b, 0x9d, 0xfa, 0xc4, 0x50, 0xf0, 0x2f, + 0x9d, 0xbd, 0xdb, 0x8c, 0x8f, 0x35, 0xff, 0xa7, 0x59, 0x3e, 0x3f, + 0x8f, 0xf2, 0xc2, 0x1a, 0xef, 0x08, 0x68, 0xb6, 0xe3, 0xfb, 0x52, + 0x4a, 0x0f, 0xf7, 0xba, 0xc9, 0xa2, 0xf0, 0xa0, 0x3c, 0xe7, 0xa6, + 0xa2, 0x2b, 0x84, 0xd1, 0x53, 0xe8, 0xc5, 0x96, 0x66, 0x8c, 0x2c, + 0x43, 0x8e, 0xba, 0xad, 0xa1, 0xec, 0x9e, 0x00, 0xa0, 0xab, 0x7b, + 0x13, 0xd2, 0x6d, 0x44, 0x36, 0xdc, 0x84, 0xd8, 0x7d, 0x02, 0x8a, + 0x64, 0x5d, 0x30, 0x42, 0xf2, 0x56, 0x55, 0xc5, 0xbc, 0xf8, 0x06, + 0xaa, 0x53, 0xbd, 0x25, 0xd6, 0x04, 0x83, 0xc6, 0xad, 0xd8, 0x16, + 0xaf, 0x38, 0x77, 0xd9, 0xbd, 0xaa, 0x10, 0xbb, 0x35, 0x09, 0xfb, + 0x03, 0x24, 0xad, 0x78, 0x92, 0x87, 0xfe, 0x81, 0xaa, 0xf0, 0x01, + 0x02, 0x30, 0x79, 0xa4, 0xb3, 0x68, 0x75, 0x1a, 0x4e, 0x1e, 0x85, + 0x1f, 0x4f, 0xde, 0x69, 0xc5, 0xb8, 0x41, 0x29, 0x97, 0xbb, 0x0c, + 0xef, 0x20, 0x22, 0x3d, 0xcb, 0xe1, 0x68, 0xa3, 0xdf, 0xbc, 0x73, + 0x74, 0x75, 0x3b, 0x22, 0xec, 0xc7, 0xac, 0x2d, 0xfb, 0x5d, 0xab, + 0xcb, 0x86, 0x2f, 0xf1, 0x0b, 0x58, 0x24, 0x66, 0x3b, 0x47, 0xac, + 0x72, 0xec, 0x26, 0x0c, 0x36, 0xc2, 0x93, 0xeb, 0x2a, 0xc8, 0x91, + 0x97, 0xfd, 0xcd, 0x54, 0xae, 0x43, 0xf3, 0x68, 0x6f, 0xdb, 0x46, + 0x8c, 0x68, 0x9e, 0x22, 0xcb, 0x3f, 0xd2, 0xfd, 0x44, 0x85, 0xc2, + 0x16, 0x71, 0xcc, 0x48, 0x91, 0x37, 0xf7, 0xfc, 0x48, 0x27, 0x1e, + 0xeb, 0x02, 0x20, 0x4e, 0xcf, 0x7e, 0xf7, 0xb7, 0x98, 0xfd, 0xe0, + 0xee, 0x27, 0xd1, 0x0f, 0x74, 0x24, 0x58, 0x2d, 0x02, 0x28, 0xd5, + 0x14, 0x84, 0xa6, 0x41, 0xfe, 0xad, 0x83, 0xd8, 0xd2, 0xd6, 0x7e, + 0xbc, 0xf2, 0x93, 0x21, 0xfb, 0x57, 0x7c, 0xc6, 0xfc, 0xa4, 0xfb, + 0x7f, 0x5d, 0x5a, 0x0c, 0xcb, 0x6a, 0xdf, 0x56, 0x5d, 0x87, 0xb6, + 0x2e, 0x3c, 0x67, 0x32, 0xec, 0x55, 0x37, 0x07, 0x30, 0x8c, 0x68, + 0x8d, 0xed, 0xa0, 0x23, 0xa7, 0x7d, 0xb1, 0x05, 0xc1, 0xfa, 0x69, + 0xaa, 0x2a, 0x90, 0x9d, 0x7b, 0xb0, 0x40, 0xe0, 0xaa, 0x37, 0x7d, + 0x76, 0x74, 0xbd, 0x38, 0xd3, 0x39, 0x77, 0x2e, 0x10, 0xc6, 0x30, + 0x86, 0x93, 0x22, 0x54, 0x84, 0xb5, 0x8a, 0x8e, 0x86, 0x33, 0xf0, + 0x31, 0x95, 0xbc, 0xaa, 0x24, 0x55, 0x3d, 0x2f, 0x4d, 0xb8, 0x6b, + 0xb8, 0x1a, 0x97, 0xbc, 0x48, 0x93, 0x2f, 0xd3, 0x46, 0xd4, 0xe3, + 0xa5, 0x2f, 0xda, 0x6d, 0xd1, 0x4a, 0xd0, 0xd6, 0xef, 0xde, 0x60, + 0x23, 0xb3, 0x3d, 0x6d, 0x65, 0x92, 0x1d, 0x14, 0xcf, 0xf0, 0xb5, + 0xfd, 0xa1, 0xa1, 0x01, 0x1c, 0x9f, 0x16, 0xce, 0x83, 0xcf, 0xe4, + 0x09, 0x29, 0xbc, 0x24, 0xe3, 0x83, 0x29, 0x29, 0x17, 0xa7, 0x31, + 0xae, 0x67, 0x63, 0x44, 0xfe, 0x7e, 0xf9, 0x4b, 0xfb, 0x2c, 0x99, + 0x28, 0x27, 0x6e, 0x44, 0x47, 0xa3, 0x7c, 0x0d, 0x8c, 0x83, 0xf2, + 0xdc, 0x83, 0x27, 0x29, 0x29, 0x2d, 0x77, 0x2f, 0x72, 0x22, 0x34, + 0x91, 0xe4, 0xe7, 0x53, 0x53, 0x1c, 0xdc, 0xbc, 0xdc, 0x4b, 0x97, + 0x86, 0x54, 0x06, 0x1c, 0xc9, 0x40, 0x79, 0x06, 0x82, 0x9e, 0x24, + 0x23, 0xcf, 0xe5, 0x64, 0x1d, 0x3e, 0x4d, 0xfd, 0xe3, 0x67, 0x79, + 0xdd, 0x62, 0x63, 0x7e, 0x97, 0xd8, 0xa0, 0xf0, 0x60, 0x20, 0xb0, + 0xeb, 0x88, 0xb9, 0x44, 0xeb, 0x38, 0xf1, 0x87, 0x90, 0xbe, 0xc0, + 0xc7, 0x0b, 0xad, 0x7b, 0x73, 0xff, 0x43, 0x9e, 0x1f, 0x77, 0xdf, + 0x17, 0x79, 0x83, 0x49, 0xd5, 0x4a, 0xf8, 0xff, 0xdc, 0x5f, 0x0d, + 0xde, 0x03, 0xd5, 0x6c, 0xfa, 0x87, 0xf4, 0xff, 0x35, 0x63, 0xf9, + 0x99, 0x5f, 0xad, 0x7c, 0xf3, 0x10, 0xbb, 0xf4, 0x8f, 0x82, 0x3d, + 0x1c, 0x09, 0x90, 0x14, 0x6d, 0x64, 0x44, 0xbf, 0xeb, 0x0e, 0xcc, + 0x98, 0x94, 0xcb, 0x61, 0xba, 0xec, 0x75, 0xb8, 0x4c, 0xc9, 0x02, + 0xe9, 0xaa, 0xd0, 0x1f, 0x44, 0x34, 0x17, 0xc8, 0xdf, 0xdf, 0x55, + 0x88, 0x39, 0x13, 0xf9, 0x88, 0x11, 0xba, 0x60, 0x2f, 0x94, 0xff, + 0x83, 0x40, 0x9b, 0x84, 0x50, 0x10, 0xc6, 0x28, 0x2b, 0x74, 0x82, + 0x98, 0x28, 0x43, 0x1a, 0x76, 0x4e, 0x01, 0x29, 0x74, 0x49, 0xe0, + 0x51, 0x8c, 0x0e, 0xc2, 0x87, 0x7a, 0x25, 0xc9, 0xbd, 0x49, 0xc1, + 0x30, 0x0d, 0xf2, 0x52, 0x60, 0xc2, 0x89, 0x7a, 0x7c, 0x7e, 0x90, + 0x7f, 0xd5, 0x31, 0x84, 0x46, 0xcd, 0xfc, 0x89, 0x02, 0x87, 0x0d, + 0x84, 0x70, 0x33, 0x33, 0x03, 0xaa, 0x72, 0x8d, 0xa7, 0x04, 0x97, + 0xf1, 0x4a, 0x45, 0xb3, 0x23, 0xd4, 0x68, 0x97, 0x6b, 0xd8, 0x23, + 0xd0, 0x11, 0xa2, 0xb7, 0x19, 0x03, 0x50, 0xc3, 0x73, 0x59, 0x10, + 0xc8, 0x88, 0x1b, 0x86, 0xee, 0x01, 0x11, 0xf7, 0x91, 0x34, 0x27, + 0x97, 0x2e, 0xa1, 0xc1, 0x05, 0x10, 0x24, 0x68, 0x54, 0xf4, 0xb7, + 0x15, 0x94, 0x3e, 0x0a, 0x79, 0x93, 0x06, 0x5a, 0x32, 0xf3, 0xc0, + 0x0e, 0x36, 0x89, 0x80, 0x2a, 0x68, 0x12, 0x12, 0xe9, 0x52, 0xb2, + 0xa9, 0x6d, 0xef, 0x97, 0xbb, 0x55, 0x7c, 0xd0, 0x0a, 0xbe, 0xc4, + 0x38, 0x01, 0x4c, 0xe9, 0xb3, 0x51, 0x80, 0x8d, 0x0b, 0xe2, 0xa9, + 0xa3, 0xb8, 0x84, 0x13, 0x93, 0x3c, 0x7d, 0x05, 0x1c, 0x4e, 0x4a, + 0xc9, 0x91, 0xd3, 0x0c, 0xa9, 0x71, 0x73, 0x93, 0xb4, 0x29, 0x11, + 0x73, 0xdb, 0x2f, 0x81, 0x5f, 0xec, 0x31, 0x6e, 0x74, 0x84, 0x4d, + 0x76, 0xc9, 0xd0, 0xe7, 0x85, 0xa2, 0xe4, 0x23, 0xa5, 0x1a, 0x77, + 0x1d, 0x88, 0x14, 0x3d, 0x06, 0xe9, 0xf0, 0xd0, 0x7b, 0xec, 0x4b, + 0x77, 0x65, 0xe4, 0xbf, 0x4c, 0xbf, 0xcf, 0xe3, 0xcc, 0xb2, 0x5a, + 0x81, 0xf8, 0x6b, 0x75, 0x98, 0x28, 0x00, 0xde, 0x51, 0x91, 0x8c, + 0x5a, 0x34, 0x08, 0x8a, 0x69, 0xb0, 0xfc, 0x8c, 0x1d, 0x22, 0x43, + 0x32, 0xce, 0x4c, 0x35, 0x6d, 0xa9, 0x9e, 0x9a, 0x35, 0x7e, 0xea, + 0x37, 0x39, 0x8b, 0xfc, 0x3b, 0x00, 0x95, 0x1e, 0x0c, 0xc3, 0x71, + 0x8c, 0xbe, 0xfe, 0xcb, 0xf8, 0xe7, 0x2b, 0x8c, 0x49, 0x10, 0xb0, + 0x76, 0xee, 0x56, 0x05, 0x69, 0x57, 0x1a, 0xdb, 0xab, 0xff, 0xba, + 0x07, 0xc8, 0xc6, 0x91, 0xc9, 0x24, 0x88, 0x09, 0x5c, 0xef, 0x0a, + 0x88, 0xbe, 0xee, 0x0b, 0x1b, 0x00, 0x26, 0x1b, 0x85, 0xd7, 0x14, + 0x91, 0xe8, 0x87, 0x36, 0x80, 0x4c, 0x18, 0x00, 0x96, 0x39, 0x37, + 0xc8, 0x3b, 0x8d, 0x19, 0x22, 0x57, 0xd3, 0x0c, 0xa7, 0x6b, 0x41, + 0x6e, 0x31, 0xa0, 0x9a, 0xcc, 0xda, 0x11, 0x0e, 0x24, 0x05, 0xac, + 0x54, 0xca, 0xc7, 0x25, 0x63, 0x94, 0x03, 0x35, 0x44, 0xa0, 0xc2, + 0xd5, 0x48, 0xb9, 0xc5, 0xab, 0x24, 0xc0, 0x84, 0x05, 0x99, 0xe8, + 0xb9, 0x4c, 0x87, 0xcb, 0x14, 0x9d, 0x4b, 0xc9, 0xb3, 0xad, 0x98, + 0x51, 0xb6, 0x52, 0xfb, 0x7c, 0x18, 0xb5, 0x64, 0x01, 0x9b, 0x96, + 0x3a, 0xba, 0x6d, 0x4a, 0xb3, 0xce, 0xc9, 0x71, 0xc0, 0xe2, 0xc3, + 0x5b, 0x43, 0x07, 0x34, 0xdf, 0x43, 0x32, 0x00, 0xb5, 0x31, 0xc0, + 0x19, 0x8a, 0x8c, 0x49, 0x8b, 0xc6, 0x6b, 0xf4, 0x1c, 0xba, 0x82, + 0x8f, 0x09, 0x07, 0x23, 0xdd, 0x4b, 0x3f, 0x79, 0xe9, 0x47, 0x65, + 0x96, 0x5f, 0x12, 0xba, 0xe7, 0x91, 0x2f, 0xd2, 0x9b, 0x91, 0x08, + 0x4f, 0x47, 0x44, 0x82, 0x08, 0xf5, 0x8e, 0xbf, 0x26, 0x48, 0x0c, + 0xcb, 0x33, 0xcf, 0x54, 0x3e, 0xf4, 0xfe, 0x43, 0x68, 0x6c, 0xf1, + 0x4d, 0x92, 0x63, 0x49, 0xa3, 0x4d, 0xc0, 0x3b, 0xff, 0x8b, 0xfd, + 0x54, 0x9e, 0x96, 0x73, 0xa6, 0xc2, 0x2d, 0xdc, 0xdc, 0xb3, 0xbc, + 0x8c, 0xff, 0xf3, 0x5d, 0x49, 0xa1, 0x66, 0x06, 0x71, 0x20, 0x26, + 0xa9, 0x8e, 0x9a, 0x7a, 0x23, 0xf9, 0xc7, 0x67, 0x60, 0x1f, 0x10, + 0xaf, 0xad, 0x83, 0x18, 0x78, 0xd1, 0x9a, 0x44, 0xf4, 0x49, 0xec, + 0x91, 0x88, 0x43, 0x55, 0xb9, 0xd6, 0x1c, 0xaa, 0x7d, 0x07, 0xb9, + 0x7d, 0xf7, 0x9f, 0xcb, 0xff, 0xfc, 0x02, 0xa8, 0x1f, 0xf7, 0xbb, + 0xbd, 0x72, 0x02, 0x4a, 0x05, 0xf7, 0xba, 0xa0, 0x10, 0x39, 0xe6, + 0xf6, 0x10, 0x6c, 0xaa, 0xf7, 0x30, 0x69, 0x94, 0x6d, 0x0e, 0x46, + 0xae, 0xcf, 0x78, 0xe7, 0x30, 0x1a, 0x13, 0xaa, 0xfd, 0x03, 0x99, + 0x87, 0x95, 0x11, 0x7c, 0x9c, 0x17, 0x54, 0x01, 0xb0, 0x2e, 0x6f, + 0x99, 0x9e, 0x76, 0xe8, 0x97, 0xd2, 0x8e, 0xa7, 0x5c, 0x8e, 0x2b, + 0x7f, 0xa6, 0x2d, 0x40, 0x87, 0x29, 0x06, 0xe6, 0xea, 0x81, 0xc1, + 0xde, 0xc7, 0xfc, 0xee, 0x65, 0x3f, 0xa4, 0x14, 0xf3, 0x2c, 0x75, + 0x27, 0x96, 0x75, 0x4f, 0x75, 0x55, 0x06, 0x94, 0x5d, 0xbe, 0x64, + 0x7c, 0x64, 0xb3, 0xc6, 0x93, 0x9e, 0x9f, 0x71, 0xa7, 0x1e, 0xea, + 0xa0, 0x77, 0xdb, 0xd6, 0x03, 0xf7, 0x19, 0xe4, 0x35, 0xcb, 0x6e, + 0x95, 0x48, 0x0f, 0x80, 0x84, 0x56, 0x37, 0xe0, 0x1d, 0x6c, 0x86, + 0x7a, 0x7f, 0x07, 0xf2, 0xa3, 0x5d, 0x91, 0x36, 0x77, 0xf0, 0x9e, + 0x33, 0xf3, 0xc0, 0x7a, 0xcc, 0x74, 0x00, 0xa9, 0x35, 0x1d, 0x49, + 0x37, 0x06, 0x07, 0x9e, 0x9f, 0x38, 0x96, 0xf2, 0x76, 0x9f, 0x94, + 0xc2, 0x73, 0xbf, 0x01, 0xd7, 0xfa, 0x40, 0x68, 0x16, 0x7c, 0x6b, + 0x28, 0xc6, 0x71, 0x12, 0x2c, 0xc0, 0x9e, 0x59, 0x78, 0x90, 0x6e, + 0xfa, 0xe2, 0xd2, 0x8a, 0x44, 0xc0, 0x93, 0xd5, 0x26, 0xe1, 0x37, + 0x68, 0x08, 0xdb, 0x2e, 0x7b, 0x0a, 0x7f, 0x7c, 0x35, 0x73, 0x92, + 0xb2, 0xd9, 0xe6, 0x28, 0x70, 0xf7, 0xe4, 0x39, 0x69, 0x7b, 0x28, + 0x60, 0xe5, 0xfb, 0xc4, 0x8d, 0x61, 0x96, 0x54, 0xed, 0xc1, 0x2b, + 0x64, 0x17, 0xa6, 0xd7, 0x79, 0xef, 0x2d, 0x31, 0x7a, 0xcd, 0x1e, + 0xfa, 0x92, 0x8a, 0x4e, 0x43, 0xcb, 0xee, 0x4f, 0x5f, 0xe1, 0xd7, + 0x07, 0x70, 0xf2, 0xa6, 0x25, 0xec, 0x67, 0x87, 0x8d, 0xa1, 0xac, + 0x3e, 0x50, 0x5f, 0xfc, 0x81, 0xfd, 0x94, 0x0f, 0x7b, 0x4b, 0xf3, + 0x68, 0xd9, 0x5c, 0xad, 0xc6, 0xd4, 0x72, 0x9f, 0xa7, 0x16, 0x09, + 0x4f, 0x54, 0x1d, 0xcb, 0xeb, 0x41, 0x8b, 0x19, 0x2d, 0xcf, 0x5e, + 0x41, 0x23, 0x93, 0xa1, 0xcd, 0x40, 0x67, 0x9f, 0x2f, 0xe8, 0xd9, + 0xcf, 0xe8, 0xc2, 0x38, 0xeb, 0x83, 0xbb, 0x51, 0xcc, 0x77, 0xd2, + 0xc7, 0x5d, 0xa5, 0xa4, 0x3d, 0x63, 0x40, 0x7f, 0x97, 0xaa, 0x3f, + 0xc7, 0xc8, 0xdb, 0x22, 0x62, 0x1c, 0xaf, 0x7b, 0x51, 0xdf, 0x47, + 0xee, 0x6c, 0xe2, 0xa9, 0x5c, 0xd6, 0x76, 0x01, 0x70, 0xb4, 0x82, + 0xfd, 0x74, 0x01, 0x28, 0x2d, 0xb3, 0x15, 0xba, 0x06, 0x4c, 0x50, + 0xf6, 0x44, 0xc9, 0x8a, 0x9f, 0x63, 0x67, 0x05, 0x0b, 0x0a, 0x48, + 0x8a, 0x73, 0x2e, 0xbd, 0xc0, 0x6d, 0x66, 0x3c, 0xfb, 0x95, 0x32, + 0x60, 0x70, 0x1f, 0x40, 0x29, 0x68, 0x61, 0x92, 0xb4, 0x98, 0x1b, + 0xb2, 0xca, 0xa4, 0x25, 0xd4, 0x73, 0x3a, 0x42, 0x58, 0xf1, 0x8b, + 0x3c, 0xf1, 0x2c, 0xd0, 0xae, 0xa7, 0x40, 0xfc, 0xd4, 0xfc, 0xd9, + 0x9e, 0x95, 0x90, 0xab, 0x4e, 0xac, 0x8f, 0xb9, 0xb2, 0x1d, 0xa5, + 0x86, 0x97, 0xc6, 0xc3, 0x28, 0x10, 0xcd, 0xb3, 0x14, 0x55, 0x33, + 0x50, 0x62, 0x17, 0x50, 0xd0, 0x10, 0xaa, 0xdf, 0x76, 0xf7, 0x8b, + 0x40, 0x17, 0xeb, 0xc1, 0x63, 0x96, 0xd0, 0x18, 0x20, 0x85, 0x96, + 0xd9, 0xc2, 0xb4, 0xfa, 0xc2, 0xbf, 0x4a, 0xc7, 0xd6, 0x92, 0x0e, + 0x35, 0x19, 0x6d, 0x96, 0x60, 0x97, 0x14, 0x69, 0xfe, 0xe2, 0x2b, + 0x02, 0xe2, 0xf3, 0x60, 0x82, 0x16, 0x26, 0xe5, 0x7c, 0xf5, 0x05, + 0xbe, 0xb5, 0xed, 0x56, 0x7a, 0xa7, 0x28, 0xe3, 0x72, 0xad, 0x62, + 0x20, 0x92, 0x4c, 0x23, 0xb4, 0xfd, 0x32, 0x6f, 0x87, 0xb0, 0x67, + 0x4d, 0x42, 0x46, 0x4d, 0xf5, 0xc5, 0xdb, 0x5d, 0xfe, 0x66, 0xfe, + 0x88, 0x23, 0xde, 0x5c, 0xf1, 0x21, 0x5f, 0xb1, 0xf3, 0x10, 0x2f, + 0xcd, 0x23, 0x41, 0x2a, 0x4f, 0xfe, 0x03, 0xa7, 0x04, 0x73, 0x30, + 0xa7, 0x74, 0x28, 0x31, 0xe3, 0x34, 0xf6, 0xf3, 0x02, 0x09, 0x0d, + 0x3a, 0x52, 0xa9, 0xd8, 0xb3, 0x17, 0x5b, 0x86, 0x54, 0x9a, 0xd1, + 0x75, 0x74, 0x77, 0xef, 0xcc, 0xa2, 0xb8, 0xb4, 0xba, 0x2b, 0x02, + 0xea, 0xa7, 0x5c, 0x9b, 0xb9, 0x43, 0xb4, 0x88, 0xc8, 0xee, 0xa3, + 0xc0, 0x6c, 0x58, 0x86, 0xb8, 0xb9, 0x9c, 0x90, 0x62, 0xe3, 0xb2, + 0xf8, 0xc5, 0x74, 0x10, 0x30, 0x37, 0xd1, 0x08, 0x0f, 0x62, 0x50, + 0x36, 0x15, 0xf0, 0x92, 0x85, 0xe2, 0xb4, 0x3d, 0x73, 0x2d, 0xee, + 0xbc, 0x62, 0x97, 0x43, 0x98, 0x04, 0x5a, 0x22, 0x8a, 0xbd, 0x6e, + 0x4e, 0x30, 0xae, 0x51, 0xd1, 0x54, 0x5e, 0xc4, 0x00, 0x78, 0xef, + 0x53, 0xbf, 0xc8, 0x0d, 0x02, 0x09, 0xcb, 0xb4, 0xee, 0xce, 0xd7, + 0x25, 0xdb, 0xfc, 0x6f, 0x29, 0xc1, 0x91, 0x50, 0x97, 0x7f, 0x39, + 0xdd, 0x53, 0xd9, 0xfa, 0xf5, 0xd0, 0xee, 0x07, 0xad, 0x4b, 0x6e, + 0x9d, 0x72, 0x4c, 0xfa, 0x92, 0xfe, 0x4b, 0x1b, 0x94, 0xe1, 0xc2, + 0x88, 0x69, 0x15, 0x41, 0x79, 0x9f, 0x9d, 0x3c, 0xa5, 0xf4, 0x51, + 0x20, 0xe6, 0x39, 0x5f, 0xab, 0x08, 0x11, 0x32, 0xb9, 0x0a, 0xc3, + 0x86, 0xf3, 0x8e, 0xb9, 0x1f, 0x95, 0x16, 0xda, 0xc3, 0x3f, 0x73, + 0x5f, 0x1b, 0x3d, 0xaf, 0x79, 0x8a, 0xf8, 0x4d, 0x70, 0x07, 0x4a, + 0x1e, 0xea, 0xf7, 0x01, 0x6e, 0xd6, 0x34, 0xca, 0xc2, 0x3a, 0x6f, + 0xe2, 0x38, 0x19, 0xd1, 0x30, 0x53, 0x39, 0x26, 0xd3, 0x26, 0x6e, + 0x64, 0xf0, 0xd7, 0xac, 0x79, 0x3a, 0x1a, 0xdb, 0x2c, 0x9c, 0x13, + 0x74, 0x9e, 0x55, 0x92, 0x9d, 0x9e, 0xaa, 0x1a, 0x0c, 0x53, 0x41, + 0x39, 0x15, 0x0c, 0x45, 0xa4, 0xbc, 0xef, 0x88, 0xdd, 0x98, 0xec, + 0xc8, 0xbf, 0x02, 0xef, 0x3d, 0x0a, 0xe1, 0x31, 0x89, 0xc6, 0x27, + 0x0d, 0x6e, 0xfb, 0x9c, 0x9f, 0x38, 0xf6, 0x79, 0x74, 0x57, 0xe4, + 0xfe, 0xd4, 0xc1, 0x87, 0x13, 0x23, 0xe2, 0xa8, 0x51, 0xa8, 0xf8, + 0x53, 0xd6, 0xf6, 0x5c, 0xa9, 0x9b, 0xb2, 0xea, 0xd4, 0x67, 0x26, + 0x42, 0x66, 0xc2, 0xef, 0xf0, 0x8a, 0x28, 0xc1, 0x60, 0xa4, 0x15, + 0xb6, 0x67, 0xb5, 0x97, 0x96, 0x91, 0xb6, 0xff, 0x1c, 0x3e, 0x96, + 0xd9, 0xed, 0x9c, 0x16, 0x3e, 0x60, 0x59, 0x64, 0xb5, 0xc7, 0x94, + 0x34, 0xca, 0xc6, 0xc6, 0xd1, 0xfe, 0x9d, 0x36, 0x49, 0xd9, 0x1b, + 0xb5, 0xf7, 0x4e, 0xad, 0xa7, 0x04, 0x8d, 0xe1, 0xcd, 0x23, 0xb8, + 0xce, 0xf6, 0xf3, 0x07, 0x76, 0xc5, 0x6f, 0xbb, 0x45, 0x1d, 0xf4, + 0x24, 0xd3, 0x93, 0x68, 0x05, 0xb3, 0xb6, 0x5e, 0xb1, 0xbe, 0x49, + 0x75, 0xf0, 0xf8, 0x54, 0x15, 0xce, 0x3e, 0x32, 0x87, 0x19, 0x39, + 0x5d, 0x89, 0x38, 0xd1, 0xd4, 0x27, 0x86, 0x0f, 0x39, 0x79, 0x8b, + 0x77, 0x5f, 0x67, 0xe1, 0xc3, 0xab, 0x80, 0x87, 0x8d, 0x26, 0x81, + 0x95, 0xfc, 0x2d, 0xbf, 0x55, 0x6a, 0xac, 0xab, 0x5e, 0x07, 0x17, + 0xaf, 0x93, 0x29, 0xac, 0xe8, 0xe9, 0x8d, 0x4b, 0x8a, 0xf5, 0x73, + 0x57, 0x44, 0xd7, 0x57, 0xdf, 0x95, 0x72, 0x05, 0x89, 0xfe, 0x36, + 0x5b, 0x88, 0x63, 0xdd, 0xc0, 0x56, 0xba, 0x20, 0xd8, 0xa4, 0x9b, + 0x71, 0x22, 0x4a, 0xb6, 0xfb, 0xf8, 0x09, 0x46, 0x92, 0x6d, 0xc4, + 0xf3, 0x10, 0x47, 0x59, 0xd7, 0x18, 0x45, 0xd3, 0x63, 0xd2, 0xa1, + 0xe5, 0x56, 0x40, 0xa7, 0x1b, 0xfd, 0x47, 0x5d, 0x41, 0xea, 0xb2, + 0x25, 0x12, 0x46, 0x14, 0xd8, 0x73, 0xa4, 0xae, 0xb4, 0xdd, 0xb5, + 0x3f, 0x74, 0x27, 0xe4, 0xb6, 0x8f, 0xb0, 0x53, 0xfc, 0x0c, 0x4f, + 0x07, 0xf0, 0x93, 0x83, 0x4b, 0xe4, 0xc6, 0x5f, 0xde, 0xc8, 0xe5, + 0xce, 0x00, 0xbb, 0x37, 0xf1, 0xe9, 0x08, 0x9a, 0xd5, 0x47, 0xa4, + 0x8f, 0xfe, 0xa6, 0x25, 0x52, 0x4c, 0x8b, 0xae, 0xbd, 0xed, 0x85, + 0x8f, 0x3d, 0x14, 0xb0, 0x66, 0x1d, 0x2c, 0x84, 0xaa, 0x24, 0xa7, + 0x14, 0x4f, 0xd6, 0x2a, 0x5f, 0xa4, 0x69, 0x75, 0x42, 0xd7, 0xae, + 0x51, 0x2a, 0xf9, 0x9f, 0xcf, 0x21, 0xfe, 0x9d, 0x8e, 0xf0, 0xfc, + 0x42, 0xbe, 0x22, 0x1d, 0x8d, 0x79, 0xb2, 0xb0, 0x3f, 0x39, 0x2f, + 0xed, 0x68, 0xa6, 0xea, 0x97, 0x5e, 0x6d, 0xf0, 0xcc, 0xa1, 0x11, + 0xf0, 0xd3, 0x39, 0x55, 0xaa, 0x76, 0xe9, 0xc3, 0xf9, 0xd1, 0x3e, + 0x23, 0xbe, 0x68, 0x58, 0x72, 0xb2, 0x60, 0xb7, 0xbd, 0xba, 0x93, + 0x6e, 0x18, 0x42, 0xa0, 0xa3, 0x6a, 0xf1, 0xe3, 0x19, 0x91, 0x40, + 0xb0, 0xa8, 0x44, 0xd7, 0xde, 0x5b, 0x13, 0x1e, 0xe0, 0xfb, 0x73, + 0x95, 0x4b, 0x81, 0x43, 0xef, 0x4c, 0x92, 0x60, 0xe8, 0x59, 0x50, + 0x8e, 0x12, 0x02, 0x36, 0x0c, 0x0a, 0x41, 0x46, 0x64, 0xf6, 0xee, + 0x47, 0x09, 0x1c, 0xd8, 0x45, 0x9f, 0x7b, 0x7f, 0xa9, 0x49, 0xf3, + 0x8e, 0xee, 0x5d, 0x77, 0x3e, 0xd9, 0x76, 0x68, 0xcd, 0x53, 0x76, + 0x2a, 0xcc, 0x34, 0xf3, 0x23, 0x65, 0xe0, 0xbe, 0xf0, 0x84, 0x72, + 0x4f, 0x1c, 0x18, 0x24, 0xff, 0x67, 0x3b, 0x70, 0x8c, 0x77, 0xba, + 0x1f, 0xc8, 0x54, 0x21, 0x56, 0x95, 0xb1, 0x43, 0xed, 0x63, 0xec, + 0x47, 0x33, 0xed, 0x07, 0x91, 0x64, 0xbc, 0x1f, 0xbe, 0xfe, 0x8a, + 0xdc, 0xdd, 0xf0, 0x20, 0x17, 0x04, 0xd9, 0x1c, 0x3e, 0x8f, 0x3e, + 0xd4, 0x21, 0x40, 0x7f, 0xf0, 0xe8, 0x0c, 0xdd, 0xf8, 0xca, 0x88, + 0x5c, 0xfd, 0xd0, 0x15, 0xe4, 0xff, 0x69, 0x05, 0x5a, 0x15, 0x10, + 0x4b, 0xed, 0x14, 0x9f, 0x83, 0xce, 0xe5, 0x2e, 0x36, 0xfa, 0xec, + 0xf8, 0xde, 0xfa, 0xc0, 0x3f, 0xb1, 0x38, 0x96, 0xea, 0x5c, 0x19, + 0xd4, 0x54, 0x7c, 0x5f, 0x70, 0xab, 0x53, 0xfe, 0x32, 0xf7, 0x9e, + 0x5b, 0x33, 0xfe, 0xfb, 0x5b, 0xbf, 0xbe, 0x13, 0xb6, 0xb3, 0x3c, + 0x1e, 0xac, 0x25, 0x3c, 0x16, 0x5e, 0x28, 0x7d, 0xaa, 0xe5, 0x28, + 0x8e, 0xbe, 0x4b, 0x97, 0x32, 0xac, 0xd4, 0x81, 0x0c, 0xda, 0x1c, + 0x9d, 0x09, 0x41, 0x27, 0xeb, 0xb0, 0xec, 0xe7, 0xae, 0x2c, 0x61, + 0x49, 0xda, 0xde, 0x46, 0x4e, 0xee, 0xbe, 0x65, 0xfd, 0xf5, 0x18, + 0xe8, 0x03, 0xe1, 0xa8, 0x53, 0x4d, 0x82, 0xb7, 0x0a, 0xcc, 0x67, + 0x7a, 0xec, 0x31, 0x8b, 0xaf, 0x1d, 0x76, 0x73, 0x4c, 0x49, 0x2c, + 0x61, 0x52, 0x92, 0xcd, 0x4e, 0xec, 0xd7, 0x40, 0xc5, 0x42, 0x74, + 0xaf, 0x97, 0xa7, 0xee, 0xeb, 0x3f, 0x2f, 0xd5, 0x8f, 0x1b, 0x72, + 0x80, 0xb6, 0x05, 0x04, 0xc0, 0xea, 0x6b, 0x5e, 0x9c, 0x5a, 0xf5, + 0xa2, 0x02, 0x7d, 0x58, 0xca, 0xa5, 0x38, 0x04, 0x2a, 0xeb, 0x19, + 0x2e, 0xc3, 0x16, 0xd4, 0x24, 0x28, 0xdb, 0x74, 0x38, 0x23, 0xb6, + 0x52, 0x68, 0x12, 0x33, 0x32, 0xdf, 0x01, 0xe3, 0x27, 0x41, 0xc2, + 0x63, 0x32, 0x7f, 0x5d, 0xc4, 0x1a, 0x5c, 0x13, 0x02, 0x32, 0x8b, + 0x2b, 0x63, 0x9b, 0x32, 0x98, 0x14, 0x69, 0xea, 0x9b, 0x50, 0x49, + 0x9c, 0x07, 0xa0, 0x50, 0x81, 0xef, 0x57, 0xfd, 0xca, 0xaf, 0xd3, + 0xe4, 0x72, 0x8c, 0x53, 0xa0, 0xb0, 0x76, 0x69, 0x17, 0xb1, 0x97, + 0x79, 0xfc, 0x50, 0x27, 0xff, 0xc3, 0xc3, 0x53, 0x6d, 0x63, 0x4b, + 0x98, 0xe8, 0xf6, 0xbf, 0x99, 0x6f, 0xa7, 0xae, 0x67, 0x34, 0xaa, + 0x5f, 0xbd, 0xd6, 0x5d, 0x97, 0x7c, 0x8e, 0x96, 0x5c, 0x1c, 0x0e, + 0x92, 0xfb, 0x63, 0x11, 0x5f, 0xa7, 0xd9, 0x23, 0xda, 0xdb, 0xde, + 0x00, 0x0f, 0xff, 0xe1, 0x60, 0x6a, 0x89, 0xd8, 0x07, 0x09, 0xa3, + 0xd0, 0xf2, 0x80, 0x4e, 0x8f, 0x11, 0xe6, 0x08, 0x73, 0x11, 0x3d, + 0xfd, 0x76, 0x73, 0x10, 0x83, 0x6a, 0x75, 0xcf, 0x0e, 0x32, 0x3e, + 0x83, 0x99, 0x7b, 0xcb, 0xaf, 0x00, 0x2c, 0xb5, 0x28, 0xdc, 0x83, + 0xf7, 0xda, 0x38, 0x5d, 0x73, 0xfe, 0x5a, 0xd8, 0xe7, 0x68, 0xed, + 0x69, 0x88, 0xe0, 0xac, 0x76, 0x64, 0xe1, 0x2d, 0x3f, 0x9f, 0xac, + 0x1c, 0x23, 0xfa, 0x2c, 0xe2, 0x4b, 0x73, 0x3f, 0x2c, 0xb0, 0xb0, + 0x31, 0x4e, 0x2b, 0xb3, 0x04, 0xda, 0xc9, 0xdd, 0xa8, 0x28, 0xda, + 0xba, 0x32, 0xc7, 0xf9, 0x61, 0xc2, 0x1f, 0x53, 0x2e, 0x42, 0xc3, + 0x6b, 0x78, 0x35, 0x34, 0x9e, 0xf3, 0x04, 0xed, 0x96, 0xc0, 0xf0, + 0x5e, 0x7b, 0xd4, 0x9d, 0x56, 0x1d, 0xa9, 0x11, 0x78, 0x18, 0xa3, + 0xb1, 0xdc, 0xa4, 0xf9, 0x28, 0xde, 0xf3, 0xb1, 0x5e, 0x9c, 0x1a, + 0x7d, 0x9a, 0x2b, 0x0f, 0x46, 0x02, 0xa3, 0x6a, 0xef, 0x73, 0x9e, + 0xd0, 0x1e, 0x36, 0x24, 0xdc, 0xf8, 0x47, 0x44, 0xec, 0xe2, 0x69, + 0x55, 0x3b, 0xdf, 0xa5, 0xde, 0xbd, 0x03, 0x10, 0x7b, 0x0a, 0x03, + 0xb4, 0xff, 0xb8, 0x84, 0xe5, 0xd0, 0xcc, 0xad, 0x76, 0x04, 0x35, + 0xb7, 0x81, 0x83, 0x26, 0x3c, 0x00, 0xe7, 0xad, 0xf7, 0xda, 0xfb, + 0x5f, 0x85, 0x3f, 0xd5, 0x5f, 0xe9, 0xc8, 0x9c, 0xf2, 0x7c, 0x4d, + 0x67, 0xfe, 0xa8, 0x0f, 0xed, 0xc2, 0xd9, 0xee, 0xd8, 0xcf, 0x40, + 0xb9, 0x50, 0x35, 0x2c, 0x38, 0xef, 0x17, 0x60, 0x0d, 0xd2, 0x3a, + 0x29, 0x43, 0x6b, 0x0b, 0xa6, 0x11, 0x5a, 0x0e, 0xea, 0xa9, 0x44, + 0x53, 0x83, 0xb6, 0x8f, 0xac, 0x9c, 0x75, 0x8e, 0x01, 0x87, 0x2d, + 0xa2, 0x3b, 0x4e, 0xd7, 0xdb, 0x33, 0x39, 0x45, 0x9e, 0x2c, 0xee, + 0x85, 0x00, 0xf3, 0x4f, 0x63, 0x7e, 0x49, 0x20, 0xb5, 0xc2, 0xce, + 0x8f, 0xa4, 0x60, 0xd7, 0x70, 0xf5, 0x7a, 0xd2, 0x12, 0x69, 0xb2, + 0x54, 0x76, 0x4b, 0xda, 0x95, 0x4b, 0xdd, 0x39, 0x1e, 0xd9, 0x89, + 0x6b, 0x25, 0xdc, 0xf2, 0xda, 0xb6, 0x0a, 0x4c, 0xc4, 0x6a, 0x8c, + 0x6a, 0xee, 0x17, 0x8a, 0x34, 0xce, 0xed, 0x20, 0xe6, 0x4a, 0x4d, + 0x93, 0x3f, 0xd7, 0x15, 0x06, 0x58, 0x00, 0x89, 0x56, 0x5d, 0x2b, + 0x4f, 0xb3, 0xbf, 0x20, 0xed, 0xa4, 0xe9, 0xaf, 0x9c, 0x87, 0x2a, + 0x0c, 0x04, 0x67, 0xa4, 0x87, 0x48, 0xec, 0x6c, 0xe8, 0xdf, 0x7c, + 0xd8, 0xaf, 0x77, 0xad, 0xf5, 0x1d, 0x75, 0x6d, 0x62, 0x01, 0xa2, + 0x0d, 0x02, 0xc1, 0x65, 0xcc, 0xab, 0xaf, 0xde, 0x5c, 0x60, 0x62, + 0x42, 0x8c, 0x90, 0x02, 0xc2, 0x23, 0x2c, 0x45, 0xab, 0xd0, 0xc0, + 0x36, 0xa5, 0x76, 0xbf, 0x35, 0x63, 0xd4, 0x82, 0x3b, 0x4e, 0xbe, + 0xeb, 0x2b, 0x1c, 0x4b, 0xb1, 0x89, 0xe5, 0x01, 0x6d, 0x68, 0xd6, + 0xc4, 0x07, 0x3d, 0xef, 0xd9, 0xd0, 0x6e, 0xc5, 0xbc, 0xb4, 0x66, + 0x18, 0x1c, 0x07, 0xe2, 0x20, 0x17, 0x6f, 0x75, 0x31, 0x70, 0xd1, + 0xf5, 0xdb, 0xbd, 0x62, 0x1f, 0xc7, 0x92, 0xae, 0xd2, 0x05, 0x01, + 0xb6, 0x97, 0x50, 0xb1, 0xd5, 0xed, 0x8e, 0xcd, 0x19, 0xb1, 0x85, + 0x23, 0xe0, 0x4d, 0x40, 0x4b, 0x71, 0xff, 0xbf, 0x2a, 0xe4, 0x57, + 0x35, 0x9e, 0x4c, 0xf5, 0xf2, 0x57, 0xcd, 0x5a, 0x3b, 0x3e, 0xd5, + 0xcb, 0x38, 0x7d, 0xd2, 0x06, 0x7d, 0xc1, 0x0b, 0x8d, 0xa1, 0xd8, + 0xff, 0x3a, 0x5c, 0xa1, 0xd8, 0x57, 0xbe, 0xd8, 0x57, 0x17, 0xeb, + 0x79, 0x9a, 0x0d, 0xc9, 0xb1, 0xf9, 0xea, 0x2a, 0xcd, 0x97, 0xd3, + 0xe8, 0xf5, 0x2e, 0x71, 0xc3, 0xe3, 0xd0, 0x38, 0x77, 0xf7, 0x5f, + 0xca, 0x28, 0xd1, 0x56, 0x6d, 0x89, 0x01, 0xce, 0x86, 0x82, 0x0f, + 0x1f, 0x6c, 0xe3, 0x4b, 0xed, 0x89, 0x7e, 0xb7, 0xd8, 0x40, 0xa1, + 0x13, 0x4a, 0xc3, 0x28, 0x86, 0xfc, 0xd7, 0x6a, 0x2b, 0xe4, 0x9d, + 0xcc, 0x98, 0x71, 0x48, 0x53, 0x71, 0xfd, 0xa4, 0x49, 0x43, 0x27, + 0xdf, 0xce, 0x9a, 0x00, 0x1c, 0x28, 0xdf, 0x2e, 0x10, 0x98, 0x5b, + 0x8c, 0xe5, 0x62, 0xfc, 0xb3, 0xef, 0x92, 0x60, 0x49, 0x73, 0xbe, + 0x7e, 0xb4, 0xfb, 0x1b, 0xbb, 0x83, 0xce, 0xdd, 0xda, 0xf1, 0xb5, + 0x77, 0xfb, 0x32, 0x35, 0x03, 0x39, 0x31, 0x5c, 0xe2, 0x70, 0x9d, + 0x3e, 0xea, 0x63, 0x9c, 0xb5, 0x59, 0xbc, 0x1a, 0x36, 0x3b, 0x12, + 0x1e, 0x5a, 0x04, 0x5a, 0x78, 0x48, 0xe6, 0x18, 0xaf, 0x52, 0x40, + 0x40, 0xd6, 0xa6, 0x02, 0x3d, 0xfe, 0xe6, 0xd2, 0xf7, 0x0d, 0xb3, + 0x1f, 0xb2, 0xf3, 0x13, 0xb0, 0xe8, 0x26, 0x19, 0xba, 0xb7, 0xda, + 0x74, 0x4d, 0xa0, 0x0e, 0x23, 0x63, 0x96, 0xf5, 0x75, 0x1b, 0x4c, + 0x98, 0xb4, 0x1e, 0x46, 0xee, 0x71, 0x60, 0x37, 0x24, 0xee, 0x73, + 0x77, 0x48, 0x72, 0x4b, 0x48, 0xc7, 0xfe, 0xba, 0x41, 0x56, 0xb5, + 0x8b, 0xef, 0xc4, 0x06, 0x0a, 0xd0, 0x93, 0x4c, 0x6a, 0xf8, 0xae, + 0x2c, 0xa8, 0x58, 0x7d, 0xbe, 0x59, 0xff, 0xb0, 0xda, 0x32, 0x3d, + 0xb1, 0xfe, 0x91, 0x2f, 0x09, 0x24, 0xee, 0x71, 0xd2, 0x72, 0xd7, + 0xe4, 0x77, 0xee, 0xca, 0xbd, 0xcc, 0xca, 0xda, 0x0d, 0x66, 0x43, + 0x50, 0x97, 0xac, 0x1a, 0x7c, 0x05, 0x49, 0x13, 0xc5, 0x0d, 0xe8, + 0xf4, 0x06, 0x6d, 0x07, 0x5b, 0xd6, 0xc5, 0x25, 0xad, 0xf9, 0x37, + 0xdb, 0x6d, 0x06, 0x6b, 0x09, 0x02, 0x1a, 0x71, 0x9c, 0x79, 0x00, + 0xa7, 0x5f, 0x01, 0x73, 0x33, 0xa6, 0xcb, 0xda, 0x32, 0x08, 0x81, + 0x6f, 0xaf, 0x39, 0x8d, 0x3a, 0xb8, 0x51, 0x72, 0x17, 0x80, 0xee, + 0xbb, 0x80, 0x74, 0x25, 0xdf, 0xce, 0xd6, 0xa0, 0xcb, 0x20, 0xb6, + 0x24, 0x7d, 0xb3, 0x5f, 0x3b, 0x0a, 0x73, 0xb5, 0x80, 0x34, 0xe5, + 0x58, 0xf5, 0xd2, 0x93, 0x34, 0xce, 0x91, 0x5e, 0xc7, 0x6b, 0xad, + 0x70, 0xf4, 0x8b, 0x05, 0xc8, 0x73, 0xaa, 0x1f, 0x84, 0xfa, 0x5e, + 0xa7, 0x5c, 0xab, 0x09, 0xf6, 0x2c, 0x9c, 0x51, 0x8d, 0x77, 0xb4, + 0x81, 0x08, 0xef, 0x74, 0x78, 0x54, 0xca, 0xd3, 0xbf, 0x1e, 0x8d, + 0xfe, 0x40, 0x53, 0x36, 0x0a, 0x30, 0x58, 0x00, 0xca, 0x2f, 0xdb, + 0x28, 0x77, 0xe3, 0x61, 0x6f, 0xdc, 0x8b, 0x5d, 0xb4, 0xad, 0x8b, + 0x3f, 0xb4, 0x3b, 0xb5, 0x15, 0x66, 0x61, 0x8c, 0x8d, 0xed, 0xb8, + 0x5a, 0xe2, 0xa0, 0x4c, 0x8b, 0x59, 0xf1, 0x3d, 0x8e, 0xe7, 0xab, + 0x7e, 0x5d, 0x63, 0x7b, 0x9c, 0x57, 0x3c, 0xfb, 0xb9, 0xdf, 0xd8, + 0x85, 0x73, 0x2f, 0xb0, 0xb0, 0x6b, 0xf4, 0x35, 0x9d, 0x51, 0xf2, + 0x94, 0x25, 0x84, 0x43, 0x10, 0x76, 0x47, 0x14, 0x4a, 0x81, 0x77, + 0xaa, 0x0a, 0xbb, 0x81, 0xb9, 0x16, 0x59, 0x5b, 0x0f, 0x5b, 0x45, + 0x3f, 0xaf, 0xf0, 0x43, 0x26, 0x1e, 0x76, 0xf5, 0x3e, 0x75, 0x79, + 0x7f, 0xd3, 0x2c, 0x8b, 0x58, 0x84, 0xbc, 0x39, 0xda, 0x19, 0xe2, + 0x9c, 0xe2, 0x16, 0x15, 0xee, 0x5f, 0x8a, 0x64, 0xc2, 0xcd, 0xca, + 0xce, 0x72, 0x81, 0x76, 0x3d, 0x1f, 0x78, 0x01, 0x5a, 0xb8, 0xde, + 0xf7, 0xf3, 0xad, 0xc8, 0xad, 0x41, 0xa3, 0x67, 0xa3, 0x4f, 0xc8, + 0x09, 0x3c, 0x6e, 0x16, 0xed, 0x69, 0x76, 0xfb, 0xfe, 0x60, 0xa6, + 0xf9, 0x47, 0x19, 0xd6, 0x4a, 0x39, 0xa9, 0x26, 0xba, 0x73, 0x4f, + 0xc2, 0xef, 0x3f, 0xe2, 0xd0, 0xac, 0x2d, 0x5e, 0xca, 0xcd, 0x8f, + 0x9a, 0x6a, 0xff, 0xe1, 0x64, 0x13, 0xdb, 0xb8, 0x17, 0x99, 0xe2, + 0xd4, 0x89, 0x94, 0x0d, 0x67, 0xc0, 0x92, 0x7d, 0x11, 0x09, 0x12, + 0x9a, 0xbc, 0xdc, 0x9f, 0x72, 0x7b, 0xd8, 0x70, 0xbc, 0x6c, 0x36, + 0x4a, 0xf5, 0x1c, 0x8a, 0x33, 0xe2, 0xd9, 0x13, 0xd3, 0x38, 0x9f, + 0x7f, 0xcd, 0x37, 0x79, 0x76, 0x52, 0xe6, 0x68, 0x9c, 0xb1, 0x5c, + 0x41, 0x96, 0x52, 0xe6, 0x98, 0x17, 0x5d, 0x2c, 0x9e, 0x22, 0x26, + 0x53, 0x3c, 0x5e, 0x75, 0xa1, 0x9d, 0xf4, 0x52, 0x4d, 0x5c, 0xb5, + 0x6e, 0x36, 0x66, 0xfd, 0x32, 0x76, 0x77, 0x79, 0x75, 0xdb, 0xd6, + 0xa9, 0x0d, 0xf5, 0x7b, 0xf9, 0x2b, 0xde, 0xb0, 0x5a, 0xdf, 0x1c, + 0xe4, 0x39, 0x9e, 0x36, 0x3a, 0x27, 0x78, 0xb7, 0xf1, 0x1b, 0xb9, + 0xe6, 0x3e, 0x58, 0xf4, 0xd3, 0x96, 0x5a, 0xb6, 0x39, 0x0e, 0x87, + 0x9d, 0x1f, 0xcd, 0xd2, 0xf6, 0xaa, 0x73, 0xdc, 0x85, 0xa2, 0x18, + 0xec, 0xcc, 0xd6, 0x6b, 0xf2, 0x46, 0x6a, 0xb0, 0xd6, 0x81, 0x62, + 0xc5, 0xd3, 0x19, 0xb0, 0xd2, 0x54, 0xcb, 0x46, 0x31, 0xe4, 0x82, + 0x3b, 0xe5, 0x30, 0xe5, 0x96, 0x7e, 0xdf, 0xe4, 0xa0, 0x51, 0x95, + 0xd8, 0x7a, 0xfa, 0xcf, 0x6b, 0x2b, 0x55, 0xa6, 0xc6, 0xde, 0xb9, + 0xc7, 0x16, 0xf0, 0x61, 0x09, 0x69, 0x9a, 0x2e, 0x57, 0x6f, 0xc2, + 0x13, 0x14, 0xdf, 0x2d, 0x6d, 0x20, 0xe2, 0x85, 0x39, 0xfc, 0xe3, + 0x85, 0x11, 0xa0, 0xdc, 0x4f, 0xbb, 0xa4, 0xc9, 0x55, 0x64, 0x66, + 0x3a, 0x57, 0x16, 0xe6, 0x81, 0x27, 0x8b, 0xd4, 0x6f, 0x76, 0x9f, + 0x96, 0x58, 0x43, 0xfd, 0x86, 0x14, 0x7b, 0xb6, 0xc6, 0x09, 0x0f, + 0xe2, 0x50, 0xf9, 0x6d, 0xfd, 0x26, 0xde, 0xae, 0x31, 0xbd, 0xb8, + 0xf2, 0xb8, 0x0d, 0xaa, 0xfc, 0xbd, 0xea, 0xf8, 0xa0, 0x6d, 0xd2, + 0x0f, 0xf6, 0x24, 0x40, 0xf1, 0xf3, 0xfc, 0x65, 0x04, 0x54, 0x7d, + 0x20, 0x06, 0x9c, 0x6c, 0x7f, 0xa2, 0xf2, 0x1c, 0xd8, 0x68, 0x5c, + 0x31, 0x9d, 0x02, 0x7b, 0xa7, 0x56, 0xf7, 0xfd, 0x65, 0x7e, 0xe8, + 0xc5, 0x7f, 0xe0, 0x5d, 0xfa, 0x1d, 0x5d, 0x7e, 0x0a, 0xde, 0xed, + 0x59, 0xc6, 0x9f, 0xaf, 0xeb, 0x19, 0x72, 0xea, 0x80, 0xe2, 0x86, + 0x27, 0xd1, 0x26, 0xed, 0xa2, 0x4a, 0x2f, 0x12, 0xa7, 0x8a, 0x67, + 0xd0, 0xfe, 0xe3, 0x14, 0xdb, 0xff, 0x98, 0x33, 0xf2, 0xbc, 0xfc, + 0x71, 0xe8, 0x7a, 0xd4, 0x35, 0xcc, 0x26, 0x28, 0x69, 0xd4, 0x15, + 0x91, 0x9f, 0xeb, 0xb9, 0xd9, 0xa9, 0x2b, 0xc3, 0xf0, 0xb3, 0x4d, + 0x22, 0xdc, 0x59, 0x19, 0xf7, 0x10, 0x89, 0x2a, 0x5d, 0x33, 0x5b, + 0x19, 0xa6, 0x31, 0xfb, 0xa6, 0xe1, 0xa5, 0x48, 0x40, 0xd0, 0x07, + 0x83, 0x9b, 0x1b, 0x85, 0x1e, 0xeb, 0x2b, 0x86, 0x99, 0x11, 0x81, + 0xc3, 0x75, 0x44, 0xf8, 0xa7, 0xb4, 0x20, 0xc5, 0x7f, 0x84, 0x88, + 0xf5, 0x84, 0x9c, 0xc2, 0x9d, 0x48, 0x86, 0xff, 0xe7, 0x46, 0xb5, + 0xa6, 0x1d, 0x3d, 0x34, 0x3f, 0x7b, 0xff, 0x74, 0x63, 0xd1, 0xfc, + 0x62, 0x27, 0x5f, 0x15, 0x30, 0x0d, 0x81, 0xf9, 0x26, 0xa7, 0x14, + 0xeb, 0xda, 0x2c, 0x74, 0x3a, 0x45, 0x45, 0x18, 0xd5, 0x70, 0x26, + 0x62, 0xfe, 0x62, 0x65, 0x2f, 0xe4, 0x1c, 0xca, 0x0c, 0xba, 0x41, + 0xab, 0xa2, 0x3a, 0x43, 0xdc, 0xf7, 0x2f, 0x1a, 0x6c, 0x0a, 0xc6, + 0xbd, 0x4e, 0x23, 0xf7, 0xa2, 0x8c, 0xbc, 0x76, 0x4f, 0xa0, 0xce, + 0xc9, 0x2e, 0x6f, 0x0f, 0x00, 0x0b, 0xbf, 0x76, 0x3a, 0xad, 0x9f, + 0x3e, 0x8a, 0x6b, 0x8e, 0xf1, 0xd1, 0xd5, 0xb2, 0x56, 0x2f, 0x56, + 0x25, 0x7f, 0x7d, 0xbe, 0x4e, 0x08, 0x19, 0x68, 0xbb, 0xcc, 0x3b, + 0xce, 0x7c, 0xaa, 0x43, 0xd2, 0x5c, 0x4e, 0xc7, 0x76, 0xc1, 0x6c, + 0xd9, 0x9d, 0x42, 0xc9, 0xaa, 0x3c, 0x64, 0xe1, 0x14, 0x4b, 0x46, + 0x24, 0x16, 0xbc, 0x2f, 0x75, 0x61, 0x77, 0xb1, 0x99, 0xfe, 0x8f, + 0xcb, 0x3c, 0xb7, 0xa5, 0x17, 0xa3, 0x5c, 0xe7, 0x55, 0x33, 0xff, + 0xda, 0xaf, 0x58, 0x1d, 0xab, 0xd1, 0x24, 0x83, 0x62, 0x41, 0x4d, + 0xec, 0xe0, 0x73, 0x01, 0xec, 0x64, 0xd7, 0x6a, 0x16, 0xd0, 0x18, + 0xfe, 0xbd, 0x35, 0x67, 0xa2, 0xf3, 0x38, 0xdd, 0xeb, 0x4e, 0x4e, + 0xae, 0x2d, 0x16, 0x65, 0x31, 0x90, 0xd7, 0x39, 0x53, 0xe4, 0xf5, + 0x0d, 0x01, 0x17, 0x00, 0x23, 0x53, 0x28, 0x70, 0x56, 0xe2, 0x5a, + 0xc7, 0x85, 0x92, 0x80, 0x29, 0xd4, 0x68, 0x48, 0x84, 0xe5, 0x7b, + 0xd8, 0xbf, 0x5e, 0xac, 0xe9, 0x04, 0x65, 0x8f, 0x97, 0x20, 0xa5, + 0x8d, 0xf2, 0x99, 0x0c, 0xc5, 0x2f, 0x9a, 0x58, 0xc4, 0x24, 0x29, + 0xd3, 0x1e, 0x59, 0xec, 0xd2, 0x7d, 0xce, 0x3b, 0x75, 0xbf, 0x3f, + 0xfa, 0x5a, 0xb8, 0xa7, 0x2a, 0x16, 0xf6, 0xed, 0xfc, 0x21, 0x6c, + 0xab, 0xaa, 0x77, 0x5b, 0xc4, 0x06, 0x58, 0x59, 0x57, 0x71, 0x53, + 0x5d, 0x54, 0xeb, 0xf9, 0x22, 0xc4, 0xa3, 0x4a, 0x55, 0x02, 0x65, + 0x40, 0x74, 0x92, 0xc0, 0xbb, 0x57, 0xd2, 0x93, 0x32, 0xf4, 0x01, + 0x11, 0xdc, 0x60, 0x6a, 0x2a, 0x7c, 0xb3, 0xc8, 0x88, 0x8d, 0x48, + 0xc9, 0x9d, 0x67, 0x74, 0x65, 0x1d, 0xa6, 0x0a, 0x44, 0x85, 0x9b, + 0x46, 0x11, 0x29, 0x34, 0x7b, 0xa2, 0x1b, 0xb8, 0x87, 0xe0, 0xee, + 0x69, 0x63, 0xf9, 0xde, 0x73, 0x73, 0xa2, 0xac, 0x53, 0xc3, 0xc9, + 0x0c, 0x4e, 0xcb, 0xce, 0x02, 0x6e, 0xec, 0x44, 0x5b, 0x33, 0x59, + 0x21, 0x9a, 0x31, 0x40, 0x69, 0x5e, 0x26, 0xeb, 0x6f, 0xee, 0x3e, + 0x8a, 0x2e, 0x18, 0x27, 0x75, 0x25, 0xca, 0xf3, 0x1b, 0xa5, 0xa6, + 0x6e, 0xae, 0xeb, 0x40, 0xc6, 0xc3, 0xa3, 0xc8, 0x38, 0x94, 0xc7, + 0x5b, 0x5e, 0x1a, 0x5d, 0xc7, 0x40, 0x5c, 0xac, 0xc4, 0x69, 0xf6, + 0x6e, 0x9f, 0x15, 0x59, 0x62, 0xab, 0x08, 0x67, 0x77, 0x53, 0x0c, + 0xce, 0x3e, 0x71, 0x1d, 0x39, 0x4d, 0x86, 0xaf, 0xb5, 0xdf, 0xd5, + 0xa8, 0x71, 0x3f, 0x3c, 0xf1, 0x67, 0x9b, 0x2a, 0x89, 0x8c, 0x27, + 0xef, 0xc9, 0x92, 0xce, 0x6c, 0xd1, 0x04, 0xbd, 0x8a, 0x0c, 0x0c, + 0x30, 0xc7, 0x72, 0x7e, 0xfa, 0xcb, 0xd3, 0x32, 0x4f, 0x95, 0x70, + 0x12, 0xd3, 0xaf, 0x2b, 0xce, 0x30, 0xf0, 0xb6, 0x65, 0x5b, 0x22, + 0xf3, 0x50, 0x10, 0x78, 0x54, 0xbb, 0x19, 0x8f, 0xf1, 0x9c, 0xd7, + 0x70, 0x16, 0x42, 0x8d, 0x78, 0xe1, 0xd4, 0xd9, 0x34, 0xa4, 0x05, + 0x98, 0x77, 0x8d, 0x29, 0x4d, 0xf3, 0xbf, 0xe7, 0x24, 0x8d, 0x1c, + 0xd2, 0xb7, 0x50, 0x29, 0x65, 0xeb, 0xaa, 0xa8, 0xcc, 0x57, 0xb8, + 0xe7, 0xeb, 0x5f, 0xe1, 0x3e, 0x69, 0x65, 0xfe, 0xb3, 0x0f, 0x11, + 0xad, 0x5c, 0xbb, 0xda, 0x0f, 0xc5, 0xee, 0x59, 0x68, 0x38, 0x50, + 0xa2, 0x1c, 0xad, 0x4f, 0x11, 0xfd, 0x54, 0x02, 0x99, 0xaa, 0x45, + 0x99, 0xb2, 0x71, 0x82, 0x9d, 0xa4, 0x82, 0x18, 0x8c, 0xfd, 0x95, + 0x75, 0x75, 0x56, 0x3c, 0x6b, 0x28, 0x75, 0x0d, 0x03, 0x15, 0xaa, + 0x17, 0xde, 0xf6, 0x9a, 0x6a, 0xdc, 0x1b, 0x7c, 0x48, 0x5f, 0xc8, + 0xfd, 0xfc, 0xfa, 0xa0, 0xa4, 0xaa, 0xce, 0x9d, 0x9f, 0x6e, 0xe2, + 0x2f, 0x38, 0xd0, 0xa1, 0xfb, 0x92, 0xaf, 0x0f, 0x9d, 0x2f, 0x60, + 0x4c, 0xeb, 0xe3, 0xc5, 0x54, 0x66, 0x60, 0xd7, 0xe3, 0x45, 0x0f, + 0xeb, 0x17, 0xda, 0x94, 0x5f, 0x6a, 0x3c, 0xfa, 0x5b, 0xa2, 0xf6, + 0xac, 0x0e, 0x94, 0x87, 0xe3, 0xf0, 0x9c, 0x1d, 0x1f, 0x0b, 0xd1, + 0xa5, 0x41, 0x89, 0x51, 0x30, 0x12, 0xac, 0xf6, 0x67, 0x27, 0x1a, + 0xea, 0x93, 0x69, 0x21, 0xe6, 0x5f, 0xdc, 0x1f, 0xf4, 0xb9, 0x8c, + 0x98, 0x9d, 0x66, 0x4b, 0xf3, 0x18, 0x45, 0x0c, 0x84, 0x7d, 0x12, + 0x1d, 0xbb, 0x6f, 0x00, 0xab, 0xbc, 0xe8, 0x53, 0x13, 0x6a, 0x82, + 0xc4, 0x6f, 0xfe, 0x5a, 0xab, 0xeb, 0x11, 0xe7, 0x74, 0x37, 0x9c, + 0x4d, 0xab, 0x23, 0x97, 0xed, 0xa6, 0x7c, 0xa7, 0xfd, 0x67, 0xc2, + 0xf0, 0xd7, 0x1a, 0x1d, 0x67, 0x63, 0x39, 0x81, 0xd3, 0x77, 0x15, + 0xfa, 0xc5, 0x6a, 0x79, 0xfb, 0x6a, 0xec, 0x42, 0x82, 0x25, 0x2a, + 0x81, 0x33, 0x7b, 0xb3, 0x1d, 0xf2, 0xd4, 0x04, 0x6d, 0x96, 0x2f, + 0xe7, 0x64, 0x7f, 0x85, 0x5b, 0xd7, 0xb1, 0xd3, 0x96, 0x86, 0x75, + 0x19, 0x41, 0x55, 0xdc, 0x8b, 0xdc, 0x86, 0xf9, 0xdb, 0xd7, 0xa6, + 0xb8, 0x99, 0x14, 0xc5, 0x9a, 0x5f, 0xec, 0x2d, 0xdf, 0x25, 0xf4, + 0xfd, 0xf3, 0xdf, 0xf5, 0xe0, 0x2d, 0xb4, 0xd7, 0x67, 0xfc, 0x2d, + 0x39, 0x0b, 0x31, 0x19, 0x95, 0x1a, 0x71, 0x06, 0xae, 0xcd, 0x2e, + 0x2a, 0xb9, 0x30, 0x0c, 0xb5, 0x1e, 0x5a, 0x3a, 0x1e, 0x41, 0x46, + 0xaf, 0xae, 0x34, 0x09, 0x2e, 0xfc, 0x00, 0x6d, 0xdf, 0x80, 0x33, + 0x65, 0x4a, 0x17, 0xaa, 0x1c, 0xf3, 0x64, 0x8a, 0x18, 0xc7, 0xa2, + 0x89, 0x0b, 0xc2, 0x4f, 0xc0, 0x38, 0xee, 0x0b, 0xff, 0x14, 0x3f, + 0x94, 0x35, 0xa8, 0x9d, 0x9e, 0xde, 0xe9, 0xe2, 0x1e, 0x56, 0xa6, + 0x70, 0xb3, 0x3c, 0x7d, 0x11, 0x4f, 0xab, 0xe6, 0xfe, 0x60, 0x44, + 0x95, 0x5b, 0x53, 0x91, 0xc1, 0xfb, 0x32, 0x77, 0x98, 0x89, 0x3f, + 0x9b, 0x14, 0x1c, 0x7a, 0x2a, 0x0f, 0x71, 0xb7, 0x5f, 0x02, 0xc3, + 0x22, 0x00, 0x1a, 0x69, 0x42, 0x06, 0xb3, 0x6f, 0xf0, 0x23, 0xae, + 0x2e, 0x98, 0x9d, 0x71, 0x93, 0x83, 0xd6, 0xb6, 0xa6, 0x1b, 0x9e, + 0x8f, 0x53, 0x3c, 0x2f, 0x9a, 0x23, 0x40, 0x76, 0x39, 0x65, 0xb4, + 0x16, 0x26, 0x62, 0x26, 0xb4, 0x39, 0x9c, 0x2d, 0x1c, 0xb0, 0xc2, + 0xe8, 0x14, 0xd1, 0x4d, 0xf0, 0xbf, 0x05, 0x8f, 0x4e, 0xe9, 0xf7, + 0xcf, 0xba, 0x63, 0x13, 0xfd, 0x35, 0xea, 0xc1, 0x69, 0x08, 0x37, + 0xf3, 0x79, 0x5f, 0xd0, 0x9d, 0x4c, 0x9b, 0x00, 0x18, 0x13, 0xe1, + 0xca, 0xc9, 0x58, 0xbe, 0xb9, 0x6e, 0x67, 0x2a, 0xf9, 0xce, 0xe3, + 0xbf, 0x6b, 0xe8, 0x2e, 0x7e, 0xb7, 0xdd, 0xad, 0xfd, 0xc3, 0x7f, + 0xae, 0xc3, 0xa1, 0x75, 0xbe, 0x7e, 0xa5, 0xd0, 0xd5, 0x1f, 0x69, + 0xd5, 0x7e, 0xbd, 0xf5, 0xa2, 0x7c, 0x53, 0x2c, 0x92, 0xd1, 0xe1, + 0x1c, 0x85, 0xd3, 0x71, 0x4e, 0x42, 0xda, 0x2d, 0x72, 0xec, 0x73, + 0x2e, 0x6c, 0x0c, 0x25, 0x47, 0x7e, 0xf2, 0x1d, 0x09, 0x3a, 0x33, + 0x8b, 0x9b, 0x4f, 0xa9, 0x7d, 0x15, 0x17, 0x7b, 0x56, 0x01, 0x2c, + 0xee, 0xa6, 0x64, 0x83, 0x35, 0xd1, 0x49, 0x8a, 0x91, 0x1e, 0x8b, + 0xf4, 0x03, 0x70, 0x37, 0xe2, 0x64, 0x3d, 0x54, 0x4f, 0x4c, 0xf4, + 0xeb, 0x84, 0xb2, 0x15, 0x96, 0x85, 0xdf, 0x71, 0x93, 0x3c, 0x1a, + 0x1e, 0xf3, 0x7d, 0x8f, 0xd6, 0x78, 0x9e, 0x0d, 0x82, 0xf1, 0x8a, + 0x86, 0x1a, 0x2c, 0x0b, 0x2f, 0xa8, 0xaa, 0x9a, 0x70, 0xce, 0x92, + 0x14, 0x31, 0x57, 0xed, 0x1c, 0x1c, 0x1e, 0x7b, 0x0c, 0xc1, 0x81, + 0x61, 0xf1, 0xbb, 0xd2, 0xc5, 0x94, 0xb5, 0x1d, 0xc7, 0x05, 0xc3, + 0xb4, 0xa5, 0x9f, 0xbd, 0xc0, 0x9f, 0x9d, 0x5f, 0x93, 0x4b, 0xce, + 0x91, 0xe7, 0xd9, 0xb8, 0xe7, 0xc5, 0x2c, 0xa1, 0x76, 0xad, 0xb2, + 0x1f, 0x18, 0x2b, 0x12, 0x1a, 0x6e, 0xa8, 0x82, 0xaa, 0x7f, 0xd3, + 0x37, 0x61, 0x85, 0x09, 0x00, 0x7f, 0x88, 0x93, 0x30, 0x76, 0xb0, + 0x87, 0xfe, 0xb8, 0x46, 0xc2, 0xe1, 0xaa, 0xba, 0x64, 0xde, 0x80, + 0x63, 0xc1, 0x53, 0xdd, 0x40, 0xab, 0x6a, 0x14, 0x91, 0xe1, 0xa0, + 0x45, 0x3a, 0x33, 0xb7, 0xbd, 0x4a, 0x04, 0x93, 0x48, 0xcd, 0x50, + 0xad, 0x43, 0xfb, 0x31, 0xb4, 0x23, 0xd1, 0x06, 0x95, 0x43, 0xc2, + 0x34, 0x28, 0xa0, 0x5a, 0xb2, 0x72, 0xb6, 0x30, 0x65, 0x13, 0x8c, + 0x89, 0xd6, 0xd6, 0xfa, 0x6c, 0x37, 0x4c, 0x5b, 0xd9, 0x45, 0x78, + 0x78, 0xa6, 0xf4, 0x73, 0x1d, 0x9c, 0xb8, 0x9c, 0xd6, 0x8a, 0x2d, + 0xb6, 0x31, 0xa4, 0xef, 0xc9, 0xbb, 0xa8, 0x93, 0x69, 0xfc, 0x2d, + 0x21, 0x5a, 0xad, 0x4a, 0x7a, 0x1d, 0x9c, 0x30, 0xe1, 0x31, 0x1f, + 0x18, 0xb6, 0xbf, 0x7b, 0xdf, 0x3b, 0x29, 0x26, 0xef, 0x27, 0x1e, + 0xe7, 0xa8, 0x6d, 0xac, 0xef, 0xd5, 0x73, 0x8b, 0xcd, 0x32, 0x86, + 0x77, 0x9f, 0x16, 0x01, 0x52, 0x33, 0x7a, 0x59, 0x84, 0xb8, 0xfc, + 0x69, 0xbc, 0x02, 0xa9, 0xe4, 0x2a, 0x1b, 0xb7, 0xfc, 0x21, 0x80, + 0x88, 0x1e, 0x5d, 0xe2, 0xaa, 0x97, 0x52, 0x37, 0xd3, 0xe8, 0x23, + 0x9a, 0x7e, 0xe0, 0x5c, 0xfa, 0x4a, 0x76, 0x0c, 0xb2, 0xb8, 0x62, + 0xe0, 0x08, 0xdc, 0xd7, 0xf3, 0xaa, 0xf0, 0x0f, 0x3c, 0x2f, 0x77, + 0x22, 0x7e, 0x51, 0xb9, 0x50, 0xda, 0x00, 0x51, 0x6d, 0x38, 0x05, + 0x2d, 0xac, 0x33, 0xee, 0xae, 0x36, 0xa0, 0xaa, 0x7a, 0x9e, 0xff, + 0xdb, 0x16, 0x5b, 0xf1, 0x39, 0x0d, 0x94, 0x70, 0x46, 0x6a, 0xf7, + 0x3a, 0x75, 0x19, 0x1a, 0x56, 0xdb, 0xe0, 0x6e, 0x21, 0x36, 0x51, + 0xe4, 0x9f, 0x71, 0xb4, 0x17, 0xd8, 0xf1, 0x05, 0x9c, 0x6e, 0x9f, + 0xfd, 0x38, 0xfc, 0x63, 0x2e, 0x49, 0xf7, 0x3e, 0xcb, 0xbe, 0xaa, + 0x6f, 0xd8, 0xc6, 0xfa, 0xb9, 0x9c, 0x05, 0x85, 0x39, 0x41, 0xc9, + 0xa3, 0xaf, 0x19, 0x83, 0x2b, 0xe8, 0x75, 0x2d, 0xfa, 0xdf, 0x8a, + 0x38, 0x85, 0xeb, 0x1a, 0xac, 0x0f, 0xc4, 0xde, 0x6c, 0xb2, 0xd7, + 0x8e, 0xa8, 0x90, 0xea, 0x92, 0x24, 0x51, 0x22, 0xbe, 0xfe, 0x63, + 0x7f, 0x71, 0x1f, 0x61, 0xa9, 0x69, 0xed, 0x4f, 0x20, 0xd5, 0x10, + 0xf7, 0xe5, 0xa3, 0xbf, 0x40, 0x3a, 0x4e, 0x1e, 0xb5, 0x36, 0xc1, + 0xd0, 0xa9, 0x47, 0x3a, 0x0d, 0xbc, 0xcf, 0x28, 0xfb, 0xca, 0xd9, + 0x89, 0x60, 0x13, 0x8e, 0x76, 0xe4, 0x36, 0x82, 0x97, 0x58, 0x1e, + 0x02, 0x8a, 0x94, 0xcd, 0x37, 0x6c, 0xf3, 0x26, 0x03, 0xe4, 0xdb, + 0xe1, 0xc6, 0xf2, 0x46, 0xe7, 0xa4, 0x67, 0x14, 0x21, 0x84, 0xbd, + 0x35, 0xb3, 0x6b, 0x24, 0x37, 0xf8, 0x86, 0x1c, 0x25, 0x9b, 0x2f, + 0x4e, 0x46, 0x09, 0x4e, 0xdc, 0xbf, 0x22, 0x74, 0xc5, 0xc2, 0xc7, + 0xd0, 0x62, 0xfa, 0x50, 0x32, 0xb9, 0x80, 0x1a, 0x1e, 0xf0, 0x10, + 0xe1, 0x8e, 0xfa, 0xd0, 0x53, 0x63, 0x4a, 0x07, 0x6a, 0xc2, 0x7a, + 0xa9, 0xbc, 0x25, 0xb2, 0x53, 0x16, 0x77, 0xaf, 0x2c, 0xf1, 0xf9, + 0x74, 0x93, 0x56, 0x55, 0x88, 0x62, 0xb8, 0x4b, 0x2b, 0x08, 0xd0, + 0xb2, 0x6d, 0x09, 0x0c, 0x57, 0x3c, 0xc1, 0xba, 0xb9, 0x6d, 0xa3, + 0x96, 0x50, 0xd5, 0xd7, 0xd5, 0x55, 0x78, 0x0d, 0xa6, 0x45, 0x9b, + 0xaf, 0x91, 0xf4, 0x05, 0x94, 0x5a, 0xc4, 0x51, 0x20, 0x17, 0xf8, + 0x11, 0x9c, 0xf1, 0x25, 0x10, 0x04, 0xe9, 0xbe, 0x33, 0xa1, 0x4b, + 0xe0, 0x74, 0xd9, 0xe9, 0x69, 0x04, 0x6f, 0xa0, 0x68, 0x6e, 0x84, + 0x46, 0x9e, 0x7f, 0x75, 0x50, 0x6a, 0x59, 0x59, 0x0b, 0xc4, 0xdd, + 0x98, 0xb2, 0x50, 0xa2, 0x0c, 0xda, 0xcb, 0x64, 0x6c, 0xd2, 0x31, + 0x87, 0xb1, 0x5f, 0x72, 0x99, 0xc0, 0x06, 0x09, 0x08, 0x49, 0x0c, + 0x18, 0xcc, 0xed, 0x9a, 0x24, 0x7e, 0x05, 0xb3, 0x6c, 0xec, 0xf3, + 0x64, 0x25, 0xcb, 0x88, 0x44, 0xb7, 0x53, 0xf7, 0xdd, 0x64, 0xd6, + 0x3c, 0x99, 0xf7, 0x97, 0x9f, 0x23, 0xfa, 0x9f, 0x65, 0x02, 0x2a, + 0x7f, 0xc0, 0xe4, 0x64, 0x35, 0x2c, 0xdc, 0x88, 0xca, 0x76, 0xc9, + 0x2f, 0x75, 0xa7, 0xb1, 0xf0, 0x9a, 0x8a, 0xa4, 0x95, 0x2d, 0xc3, + 0xbe, 0xfc, 0xd4, 0x49, 0x3a, 0xf8, 0x3a, 0x85, 0x09, 0x5f, 0x1f, + 0x0f, 0xcf, 0xa1, 0xd6, 0xe9, 0x7a, 0x21, 0x05, 0xe6, 0x7a, 0xc4, + 0xb2, 0x95, 0xb1, 0xb6, 0xe8, 0x00, 0xa8, 0x7f, 0x97, 0x16, 0xec, + 0x1d, 0x73, 0x93, 0xc3, 0x36, 0xe2, 0xb3, 0x40, 0x6c, 0xa8, 0x79, + 0x88, 0x39, 0xe7, 0xe6, 0xdc, 0xd8, 0xd2, 0x43, 0x2f, 0xcd, 0xc7, + 0xeb, 0x47, 0x99, 0xd5, 0xe5, 0xf7, 0x7f, 0x7b, 0x6b, 0xd9, 0x6b, + 0x28, 0x6c, 0x6a, 0x49, 0x79, 0xdf, 0x46, 0x38, 0x9e, 0x75, 0xb2, + 0xe0, 0xdf, 0xcc, 0x1c, 0x35, 0xc7, 0x25, 0x68, 0x1b, 0xfb, 0x22, + 0xe0, 0x3f, 0x9f, 0xde, 0xd3, 0x4a, 0x85, 0x78, 0x56, 0xc3, 0x17, + 0xd1, 0x1e, 0x46, 0x3c, 0x4f, 0xcf, 0xb7, 0x01, 0x29, 0x50, 0x40, + 0x87, 0x54, 0xa5, 0x55, 0x34, 0x14, 0x60, 0x1f, 0xf3, 0xf8, 0x86, + 0x88, 0x10, 0x2a, 0x22, 0xd6, 0x0e, 0xd0, 0xf0, 0x59, 0x4a, 0xf2, + 0x2c, 0x96, 0x83, 0xfa, 0xad, 0xa6, 0x1a, 0xe3, 0x87, 0x5c, 0x5b, + 0x13, 0x4c, 0x74, 0x95, 0x95, 0x93, 0x6a, 0x5e, 0x94, 0x1b, 0x4b, + 0x4a, 0xb4, 0xbd, 0x02, 0x4c, 0x9e, 0xfb, 0xfa, 0x01, 0xf6, 0xed, + 0xb2, 0xf6, 0x49, 0x95, 0x83, 0xaf, 0x71, 0xe2, 0xb7, 0x01, 0xfc, + 0x7c, 0xa9, 0xac, 0xda, 0x0f, 0x5b, 0x24, 0xd4, 0x56, 0x57, 0x7d, + 0x0c, 0x80, 0x38, 0xbf, 0xea, 0x22, 0x50, 0x61, 0xc1, 0x2f, 0x51, + 0x8b, 0x6b, 0x99, 0xc5, 0xd3, 0xa0, 0x1b, 0xe1, 0xa9, 0xba, 0xaf, + 0xf6, 0xf1, 0x18, 0x04, 0x3c, 0x1d, 0xd0, 0xca, 0x6a, 0xaf, 0xa1, + 0x46, 0x90, 0x51, 0x11, 0xc2, 0x6d, 0x53, 0x52, 0x23, 0xfe, 0xf3, + 0xa1, 0x66, 0x66, 0xfb, 0x59, 0x80, 0xe8, 0x50, 0x8e, 0xf8, 0xb9, + 0x2b, 0x10, 0x30, 0x90, 0x94, 0x59, 0x8e, 0x39, 0x08, 0xde, 0x9e, + 0xda, 0xbe, 0x03, 0xde, 0xdc, 0x3b, 0x59, 0x3e, 0xf5, 0xf1, 0xfa, + 0x0d, 0x3d, 0xb5, 0xfa, 0x37, 0x3c, 0x8a, 0x36, 0xbc, 0x7f, 0x95, + 0x98, 0xdd, 0x75, 0x0a, 0x47, 0x05, 0x47, 0x3c, 0x83, 0x44, 0xee, + 0xcb, 0xe0, 0x0b, 0x4d, 0xbd, 0x73, 0x2b, 0xe2, 0x9f, 0xe3, 0x63, + 0xa1, 0x3d, 0x05, 0xea, 0xa5, 0x9c, 0xca, 0x96, 0xd3, 0x39, 0xc8, + 0x8f, 0x12, 0x97, 0x59, 0x3c, 0xae, 0xd0, 0x69, 0xf0, 0x79, 0x08, + 0xa0, 0x85, 0x37, 0xcc, 0x16, 0x6c, 0xfb, 0xa1, 0x64, 0xf1, 0x38, + 0x91, 0x55, 0x0b, 0x21, 0x18, 0x10, 0xd8, 0x8d, 0xd3, 0xdd, 0xad, + 0x61, 0xa6, 0x58, 0xcb, 0x86, 0x31, 0x56, 0x57, 0x6b, 0xa1, 0x9d, + 0x8b, 0x9e, 0x9e, 0x15, 0x81, 0x2f, 0x20, 0x86, 0x81, 0x40, 0x1c, + 0x96, 0x8e, 0x2e, 0xf1, 0xd5, 0x97, 0xf8, 0x93, 0x06, 0xd9, 0x12, + 0x31, 0x2f, 0xe5, 0x09, 0xf2, 0x43, 0x6a, 0x21, 0x85, 0x47, 0xee, + 0x20, 0xec, 0x94, 0x4e, 0x1a, 0xc4, 0x27, 0x83, 0x9a, 0x65, 0x1f, + 0xc8, 0x0f, 0x2e, 0x37, 0xeb, 0x4d, 0x9b, 0xb5, 0x16, 0x35, 0xeb, + 0x8f, 0xeb, 0xa4, 0xef, 0x73, 0x66, 0x1a, 0x39, 0xd3, 0x04, 0x5c, + 0x61, 0xa1, 0x17, 0x36, 0xe1, 0x34, 0x7d, 0x91, 0x5c, 0x28, 0x48, + 0x6e, 0x9e, 0xf6, 0x9a, 0xcb, 0x6b, 0xfd, 0xf7, 0xcb, 0x19, 0x3c, + 0x5a, 0x77, 0x51, 0x52, 0x48, 0x12, 0x7f, 0xc0, 0x63, 0x14, 0x5f, + 0xae, 0x07, 0x9e, 0x7f, 0x00, 0x83, 0x1b, 0x8e, 0xbc, 0x20, 0x3e, + 0xc2, 0x9d, 0xa2, 0x5b, 0xa1, 0x29, 0x8d, 0x00, 0xf8, 0x69, 0xfb, + 0x31, 0x51, 0xc9, 0x86, 0x89, 0xfe, 0x48, 0xed, 0x69, 0x7a, 0x96, + 0x45, 0xbd, 0xcc, 0x93, 0x74, 0x5b, 0xc4, 0x74, 0xbb, 0x40, 0xe7, + 0x12, 0xde, 0xf5, 0x18, 0xef, 0x9b, 0x0d, 0x05, 0xd0, 0x68, 0x3c, + 0x50, 0xc8, 0xc8, 0xff, 0x10, 0x69, 0x78, 0xbf, 0x8e, 0x1d, 0xad, + 0xdf, 0xbd, 0x6c, 0x17, 0xb9, 0x34, 0x39, 0x08, 0x6e, 0x8a, 0x7d, + 0xce, 0x58, 0x50, 0xc8, 0x4d, 0xc2, 0x28, 0xed, 0x32, 0xeb, 0x60, + 0x74, 0x05, 0x58, 0x9e, 0x6a, 0xdd, 0xc3, 0x49, 0xe9, 0xb1, 0x84, + 0x8e, 0x1b, 0x20, 0xb3, 0x38, 0xe3, 0x45, 0x70, 0x88, 0x26, 0x8f, + 0x9d, 0xab, 0xf9, 0x41, 0xf4, 0x8f, 0x09, 0x3f, 0x73, 0x92, 0x8a, + 0xfa, 0xc4, 0xb0, 0xc2, 0x2f, 0x30, 0xed, 0x96, 0x6f, 0xcd, 0xaa, + 0x7f, 0xb7, 0x32, 0x4a, 0xe0, 0x6b, 0x2f, 0xcf, 0xbf, 0x75, 0x94, + 0x3b, 0x01, 0x8d, 0x08, 0xc4, 0x43, 0x86, 0x5e, 0x4e, 0x6e, 0xe0, + 0x8c, 0xb9, 0x4d, 0x2b, 0x2c, 0x46, 0x16, 0xd4, 0x40, 0x1b, 0x48, + 0x63, 0x93, 0xbe, 0x18, 0x12, 0xdd, 0x90, 0x1b, 0xbe, 0x5c, 0xd3, + 0xe3, 0x72, 0x01, 0xf5, 0x6f, 0x79, 0x0e, 0xb6, 0x0a, 0x66, 0xb8, + 0x38, 0x03, 0x20, 0xb2, 0xf1, 0xaa, 0xac, 0x62, 0x50, 0xe5, 0x6e, + 0xea, 0xc6, 0x0e, 0xa3, 0xa6, 0x47, 0xa6, 0xf2, 0x9e, 0x49, 0xbb, + 0x9d, 0x60, 0xd7, 0x4d, 0x95, 0x68, 0x55, 0x93, 0x5e, 0x97, 0x67, + 0x82, 0x6d, 0x49, 0xa9, 0xeb, 0x70, 0x10, 0xa7, 0x57, 0x88, 0x24, + 0x0a, 0xd5, 0x5d, 0xfa, 0x11, 0x92, 0x78, 0x1e, 0xa2, 0x36, 0x80, + 0xca, 0x18, 0x2c, 0x1e, 0x91, 0x48, 0x66, 0xd6, 0x98, 0x65, 0xc4, + 0xb6, 0xe5, 0xf2, 0xab, 0x35, 0x51, 0x3f, 0x0e, 0xb8, 0xc8, 0x83, + 0x95, 0x2c, 0xb9, 0xeb, 0x3d, 0xb8, 0x0b, 0x91, 0xdb, 0x51, 0x32, + 0x36, 0x70, 0x9e, 0x68, 0x0f, 0xbc, 0x24, 0xd9, 0xff, 0x2f, 0x13, + 0x87, 0x08, 0x87, 0xa3, 0xc5, 0xa4, 0x94, 0x8f, 0x39, 0x3f, 0xce, + 0x0b, 0x33, 0x38, 0xd7, 0x82, 0x96, 0x21, 0xf0, 0xe6, 0x0d, 0xa1, + 0xa3, 0x99, 0xf1, 0x88, 0x05, 0xe4, 0x33, 0xb1, 0xa3, 0xb4, 0xea, + 0x9a, 0xef, 0xdc, 0x39, 0xaf, 0x0c, 0x74, 0x8f, 0x3b, 0x9d, 0x97, + 0xca, 0xbd, 0x73, 0xce, 0x55, 0x2f, 0xf5, 0xb1, 0x03, 0xc6, 0x8a, + 0x07, 0x51, 0x83, 0x33, 0x9e, 0x06, 0x84, 0x6e, 0xb7, 0xa7, 0x72, + 0x8a, 0xad, 0x9d, 0xcb, 0xe5, 0x5f, 0xf8, 0xfa, 0x01, 0x3d, 0x0c, + 0xb0, 0xa6, 0x1d, 0xc5, 0x1e, 0x6d, 0xd5, 0x2c, 0xb0, 0xc5, 0x6b, + 0xf1, 0xfe, 0x11, 0xb5, 0x98, 0x04, 0x0b, 0x82, 0x0f, 0x4e, 0x0b, + 0x92, 0x04, 0xb7, 0xe6, 0xc1, 0x1e, 0x50, 0x5e, 0x26, 0x7e, 0x42, + 0x4b, 0x9b, 0x01, 0x24, 0xe9, 0x0d, 0xe8, 0xfb, 0x95, 0xb2, 0xba, + 0x95, 0xad, 0xd6, 0x0d, 0x31, 0x3f, 0x9c, 0x0a, 0x28, 0x53, 0x40, + 0xfe, 0xb0, 0xfb, 0x9f, 0xac, 0x56, 0x94, 0xce, 0xd0, 0x31, 0x44, + 0x6d, 0x38, 0x97, 0x59, 0x3f, 0xa3, 0xfc, 0xf5, 0xc1, 0x9f, 0x9c, + 0x3c, 0xd7, 0x76, 0x43, 0xc3, 0xd0, 0x7b, 0x0c, 0xbf, 0xa7, 0x99, + 0x4d, 0x89, 0x10, 0x53, 0x30, 0x36, 0xf2, 0xde, 0xf2, 0xa9, 0xc7, + 0x76, 0xe3, 0x41, 0xf2, 0xfa, 0xcd, 0xf7, 0xfe, 0x00, 0xf2, 0xd8, + 0x29, 0x1c, 0x01, 0x81, 0x25, 0x9b, 0x64, 0x50, 0x51, 0xe4, 0x63, + 0x63, 0x91, 0xcb, 0x4c, 0x88, 0xa8, 0x00, 0xea, 0xc0, 0x9c, 0x31, + 0x0a, 0x9c, 0x92, 0x92, 0x8f, 0x86, 0xb0, 0x83, 0x00, 0xeb, 0x42, + 0x00, 0x1b, 0x66, 0xa0, 0xed, 0x2d, 0xc3, 0x43, 0xfe, 0x5f, 0xa6, + 0xb6, 0xaa, 0xf6, 0x8f, 0x9b, 0x7d, 0xcf, 0x44, 0x76, 0xc1, 0x41, + 0x86, 0x3b, 0x5b, 0xe8, 0x6c, 0x5a, 0xb9, 0x73, 0xe0, 0x9f, 0xa4, + 0x37, 0x32, 0x97, 0x38, 0x72, 0x51, 0x88, 0x01, 0xb8, 0x18, 0x51, + 0x5d, 0x82, 0xa1, 0xca, 0xa5, 0x92, 0x42, 0x47, 0x24, 0xe8, 0x6f, + 0x88, 0xf0, 0xd2, 0x42, 0x0c, 0x1b, 0xdf, 0x16, 0x45, 0x24, 0x7b, + 0xe8, 0x44, 0x49, 0xf4, 0xf4, 0x13, 0x00, 0x2a, 0xa5, 0x68, 0xf9, + 0x30, 0x01, 0xde, 0xcf, 0xa5, 0xe7, 0x1a, 0xe3, 0x6c, 0x09, 0x8a, + 0xb7, 0x2d, 0x69, 0x80, 0x1e, 0x8d, 0x37, 0x4c, 0xa2, 0x40, 0xb4, + 0x1d, 0x47, 0x7b, 0x71, 0xde, 0xc7, 0xe0, 0x28, 0x32, 0xd5, 0xcf, + 0xf1, 0x99, 0x6f, 0x7d, 0x10, 0xb4, 0x34, 0xe1, 0xf9, 0x16, 0xf1, + 0x71, 0x17, 0x5d, 0x76, 0xe2, 0xc6, 0x02, 0x91, 0xbe, 0xa7, 0xfb, + 0x35, 0x86, 0x51, 0xe4, 0xb4, 0x1f, 0xc5, 0x44, 0xd5, 0x85, 0x61, + 0xdf, 0x0a, 0xc5, 0x62, 0x9c, 0xf3, 0x11, 0xdc, 0x69, 0x1e, 0x12, + 0xa6, 0x59, 0x0e, 0xa5, 0xc7, 0x99, 0xfe, 0x74, 0x57, 0xaa, 0x07, + 0x2e, 0x9e, 0x6a, 0xb2, 0x43, 0x9c, 0x13, 0x82, 0x1b, 0x10, 0x18, + 0x8c, 0x4d, 0xa7, 0x36, 0xf8, 0x5a, 0xf6, 0x9f, 0x4e, 0x84, 0x12, + 0x20, 0x11, 0xb0, 0xbc, 0x15, 0xae, 0xea, 0xcd, 0xde, 0xa9, 0xff, + 0xc8, 0x28, 0xc3, 0x82, 0x5a, 0x6c, 0x3b, 0x10, 0xda, 0x0b, 0xe8, + 0x05, 0xd3, 0x74, 0x43, 0xce, 0x37, 0x81, 0xf6, 0x2b, 0x83, 0x75, + 0x54, 0x6c, 0x4a, 0xfe, 0x45, 0xd8, 0xd8, 0x56, 0xf9, 0xc1, 0xb6, + 0xe9, 0x21, 0x22, 0xee, 0x00, 0x72, 0x9b, 0x37, 0xd3, 0x21, 0x60, + 0x71, 0xc0, 0x2b, 0x20, 0x9d, 0x13, 0x6c, 0xce, 0xdf, 0x53, 0xb6, + 0xad, 0xdb, 0x77, 0xba, 0x68, 0xef, 0x6c, 0xff, 0x99, 0xbd, 0x44, + 0xeb, 0xda, 0xa6, 0x01, 0xbc, 0x1a, 0x73, 0x1a, 0xc1, 0x5a, 0x0c, + 0x0b, 0xd9, 0xe4, 0xda, 0xb5, 0x73, 0xcb, 0xfa, 0x48, 0xaf, 0x4c, + 0xf7, 0xf3, 0xa0, 0xe6, 0xb5, 0xae, 0x52, 0x4c, 0xa7, 0x3f, 0x98, + 0x26, 0x59, 0xb9, 0x68, 0x16, 0xbb, 0xe7, 0x86, 0x44, 0x4c, 0xe9, + 0x91, 0x1d, 0xc7, 0xd5, 0x69, 0xf3, 0x39, 0x9d, 0x05, 0x99, 0xe6, + 0xf1, 0xda, 0xc4, 0xcd, 0xa8, 0x5e, 0xaa, 0x69, 0x52, 0x38, 0xf0, + 0xbe, 0xba, 0x7d, 0xaa, 0x5b, 0x13, 0x9c, 0x24, 0x84, 0xc8, 0x27, + 0x40, 0x1a, 0x1a, 0x60, 0xb0, 0xb1, 0x11, 0x59, 0xf2, 0x94, 0x31, + 0xdc, 0xe7, 0x8f, 0xf6, 0x1d, 0x6b, 0xe6, 0x12, 0x70, 0xf8, 0x17, + 0x77, 0xb5, 0x03, 0xc9, 0x91, 0x9a, 0xe0, 0x7b, 0x7e, 0xcb, 0x8e, + 0xd9, 0x33, 0x50, 0x15, 0x5d, 0xad, 0xb3, 0xcc, 0x7c, 0xe7, 0xda, + 0x26, 0x48, 0x2b, 0x0c, 0x80, 0x7f, 0xc0, 0x4d, 0xc9, 0x64, 0x95, + 0xdc, 0xba, 0xdd, 0x58, 0x6d, 0x36, 0x15, 0x8b, 0x07, 0x70, 0x85, + 0x3a, 0xfd, 0xa7, 0xb8, 0xfa, 0x35, 0x41, 0xdc, 0xea, 0x03, 0xc3, + 0xca, 0x4e, 0xcc, 0xb5, 0x80, 0x31, 0xf4, 0xe8, 0xd4, 0x21, 0xd5, + 0x3d, 0xd2, 0xa5, 0x19, 0xf6, 0xf6, 0x23, 0x0d, 0xbf, 0x9f, 0x31, + 0x88, 0xd7, 0x03, 0xd8, 0x04, 0x8b, 0x91, 0xde, 0x7a, 0xb1, 0x03, + 0x91, 0xf1, 0xee, 0x29, 0xf8, 0x12, 0xdc, 0x59, 0xd6, 0xe7, 0xbf, + 0xda, 0xe0, 0x80, 0x2c, 0x46, 0x55, 0x8b, 0xa0, 0x12, 0x05, 0x98, + 0x7e, 0xd8, 0x67, 0x77, 0x73, 0x40, 0x40, 0xf6, 0x91, 0x58, 0xda, + 0x96, 0x86, 0x68, 0xf0, 0x1d, 0xbe, 0xf1, 0xbc, 0xc6, 0x81, 0x36, + 0xc1, 0x7f, 0x96, 0x19, 0x3b, 0x26, 0x39, 0xd9, 0xfc, 0x5a, 0x97, + 0x03, 0xe8, 0x0d, 0xf5, 0xd8, 0x1c, 0xb3, 0xc8, 0xdb, 0xb2, 0x97, + 0x8f, 0x27, 0x39, 0xfb, 0x67, 0x74, 0xf6, 0x1a, 0xff, 0xab, 0x05, + 0x18, 0x7c, 0x3d, 0xce, 0xbc, 0x68, 0xfa, 0x61, 0x9d, 0x7d, 0x41, + 0x28, 0xe5, 0xff, 0xba, 0x66, 0x9d, 0x6e, 0x16, 0x52, 0x13, 0x18, + 0x0b, 0xb2, 0x21, 0x37, 0x13, 0x9d, 0xe3, 0xbb, 0x4b, 0x21, 0x02, + 0x8b, 0xe5, 0xc4, 0x7d, 0xb3, 0x34, 0x4b, 0xc7, 0xa2, 0x1c, 0xea, + 0x3d, 0xbf, 0xc7, 0x38, 0x59, 0x04, 0xb2, 0x35, 0xc8, 0xe3, 0x0e, + 0x91, 0xf4, 0xec, 0xe3, 0xc0, 0x9d, 0x8f, 0xe5, 0xac, 0x4c, 0xa0, + 0x8c, 0xc7, 0x60, 0x13, 0x02, 0x23, 0xd8, 0xed, 0x05, 0xd2, 0x98, + 0xb6, 0x27, 0x77, 0x65, 0xac, 0x40, 0xb0, 0x7d, 0x1b, 0xe9, 0x75, + 0xad, 0x80, 0xee, 0x46, 0xd6, 0x66, 0x81, 0xb5, 0x5f, 0x95, 0x21, + 0x58, 0xcf, 0x8d, 0xfa, 0x31, 0x23, 0x51, 0xcc, 0xf8, 0x51, 0x7f, + 0x70, 0x62, 0x50, 0x52, 0x95, 0x8b, 0xb3, 0x0e, 0x39, 0xfd, 0x93, + 0x6b, 0x9b, 0x3b, 0x33, 0xae, 0x0c, 0xa8, 0x2d, 0x56, 0xcf, 0xb6, + 0xff, 0x22, 0x4e, 0xce, 0xb3, 0x04, 0x07, 0x3d, 0xf5, 0xe3, 0xf8, + 0xf4, 0x98, 0x0c, 0xcf, 0x3f, 0xbe, 0x78, 0xad, 0x9f, 0x3f, 0xbf, + 0xee, 0xbf, 0xb6, 0xba, 0xbd, 0x3e, 0xe2, 0x93, 0x7e, 0x30, 0x84, + 0x80, 0x56, 0x66, 0x71, 0xb5, 0x7f, 0x84, 0x41, 0x50, 0x8a, 0x40, + 0xc0, 0x18, 0x77, 0x21, 0x5a, 0xa9, 0xb0, 0xd7, 0x77, 0x6f, 0x92, + 0xab, 0xfb, 0x76, 0xe9, 0x88, 0xba, 0xf9, 0x3b, 0x19, 0x2b, 0xbb, + 0xf5, 0x4f, 0x8b, 0x8b, 0x00, 0x96, 0x25, 0x1b, 0xcc, 0x88, 0xbf, + 0xce, 0x3a, 0xf4, 0x0f, 0x78, 0xa6, 0x17, 0x8f, 0x4b, 0xb1, 0x09, + 0xcd, 0xfa, 0x7e, 0x14, 0x4b, 0x5d, 0xc0, 0x9b, 0x83, 0x69, 0x69, + 0xa1, 0x34, 0xc3, 0x60, 0x73, 0x4e, 0x54, 0x1d, 0xc3, 0x85, 0x47, + 0xe9, 0xf2, 0x77, 0x80, 0x8b, 0x30, 0xd4, 0xf6, 0x41, 0x64, 0xa3, + 0x70, 0x35, 0xd6, 0xfe, 0x3e, 0x65, 0x63, 0x0b, 0x90, 0x4d, 0xeb, + 0x5d, 0xd9, 0xb0, 0x3b, 0xa4, 0xa9, 0x53, 0x61, 0xea, 0x5a, 0x9f, + 0xcc, 0xb1, 0xed, 0x54, 0xcb, 0x50, 0xc8, 0x6f, 0xa6, 0xf1, 0x66, + 0x20, 0x89, 0x12, 0x8e, 0x7b, 0xed, 0xcc, 0xdd, 0xfd, 0x28, 0x5e, + 0x3c, 0xdf, 0xd4, 0xd0, 0xb9, 0x31, 0xe3, 0x29, 0x25, 0xa7, 0x36, + 0xb4, 0xed, 0x0b, 0xaf, 0xaa, 0x3b, 0x3f, 0xd1, 0x84, 0xdd, 0xa6, + 0xe4, 0xf5, 0x50, 0xa1, 0x6c, 0xc1, 0x5b, 0x67, 0xb0, 0x78, 0xab, + 0xa6, 0xd7, 0x2c, 0x98, 0xb6, 0xc2, 0xbf, 0x99, 0x30, 0x37, 0xd1, + 0x33, 0xd6, 0xd4, 0x55, 0x01, 0x2c, 0x3e, 0x77, 0x2e, 0x57, 0x09, + 0xf0, 0xbf, 0x24, 0x8b, 0x16, 0xe5, 0x0f, 0xb7, 0xb1, 0x86, 0x15, + 0x16, 0xc7, 0x0b, 0x24, 0xee, 0x9f, 0xde, 0xff, 0x10, 0x9c, 0xe1, + 0x41, 0x45, 0x46, 0x48, 0x7d, 0xfd, 0x24, 0x3b, 0xe9, 0x42, 0x69, + 0x8f, 0xd2, 0x4c, 0xd8, 0xf2, 0x17, 0x13, 0x54, 0x20, 0x8f, 0xdd, + 0xf3, 0x57, 0x0a, 0x17, 0x33, 0xdf, 0x56, 0x8c, 0x6f, 0x64, 0xf1, + 0x06, 0xc0, 0xb6, 0xf9, 0xb4, 0xa2, 0x88, 0x4a, 0x3b, 0x86, 0x9c, + 0x78, 0x15, 0xec, 0x58, 0x2a, 0xc6, 0x87, 0xa8, 0x56, 0xcd, 0x02, + 0x64, 0x92, 0x71, 0xd4, 0xd5, 0x67, 0x22, 0x14, 0xe9, 0x39, 0x23, + 0xde, 0x1f, 0x6b, 0x04, 0x8a, 0x2b, 0xd3, 0x5d, 0xb4, 0xf2, 0xbf, + 0x50, 0x9b, 0xbf, 0x21, 0x6d, 0x86, 0xea, 0x3e, 0x87, 0x36, 0xd7, + 0xee, 0xe3, 0xbc, 0xae, 0x68, 0x4d, 0xb1, 0xef, 0x1c, 0x29, 0x7f, + 0x82, 0xf3, 0xda, 0x19, 0x1c, 0xde, 0x53, 0x44, 0x9f, 0x2e, 0x4b, + 0x97, 0xe6, 0xec, 0x9a, 0x1e, 0x76, 0x9b, 0x77, 0x1f, 0x4a, 0xe7, + 0xf2, 0x2c, 0xa4, 0xd8, 0x00, 0x6d, 0x58, 0x32, 0x1a, 0xba, 0xaf, + 0xa5, 0x94, 0x99, 0x23, 0x3d, 0x5f, 0x62, 0x0e, 0x5e, 0xcb, 0x30, + 0xdc, 0x93, 0x4e, 0x9d, 0xf6, 0x01, 0x16, 0x56, 0xb9, 0x01, 0x46, + 0x12, 0x12, 0x0c, 0x4f, 0x05, 0x0d, 0xc5, 0x8f, 0xb9, 0x49, 0xc8, + 0xe3, 0x56, 0x32, 0xa4, 0xb4, 0xfd, 0x21, 0x87, 0xe4, 0xd9, 0xe1, + 0xe2, 0x69, 0xfe, 0x23, 0xd2, 0x0f, 0x23, 0x89, 0x90, 0xbf, 0x04, + 0xd6, 0x46, 0x03, 0x00, 0xe7, 0xc0, 0x2f, 0xc9, 0x6b, 0x2b, 0x72, + 0xfa, 0x1b, 0x4e, 0xe5, 0x97, 0x29, 0xea, 0xac, 0x5e, 0x62, 0xa1, + 0xc9, 0xea, 0xba, 0xc6, 0xd9, 0xbf, 0x8b, 0xab, 0x53, 0xdc, 0x66, + 0xc7, 0xd8, 0x78, 0xc7, 0x02, 0x0b, 0x87, 0x9d, 0x73, 0xbb, 0x83, + 0xca, 0x87, 0x09, 0x79, 0x05, 0x83, 0xb3, 0x25, 0x7b, 0x49, 0x08, + 0x49, 0x8a, 0x75, 0x6d, 0xa3, 0x37, 0xdb, 0xb5, 0x5e, 0x4b, 0x03, + 0x68, 0xc1, 0xac, 0x89, 0x92, 0x6b, 0xdb, 0x40, 0xe5, 0x89, 0x1d, + 0x23, 0xfe, 0x6e, 0x83, 0x13, 0x9a, 0x38, 0x24, 0xa3, 0x63, 0xce, + 0xa4, 0xde, 0x56, 0xd5, 0x98, 0xaf, 0xff, 0xa1, 0x0f, 0xc2, 0x9a, + 0x7d, 0xf4, 0x5e, 0xff, 0xe4, 0x49, 0xca, 0xc5, 0x87, 0x6e, 0xd5, + 0x9f, 0x68, 0xbf, 0x7b, 0xbe, 0x9b, 0x8a, 0x15, 0x9c, 0xe9, 0xf3, + 0x22, 0x68, 0x9c, 0x72, 0x64, 0x01, 0x32, 0xc7, 0xec, 0x85, 0xbc, + 0xb1, 0x77, 0x26, 0x47, 0x4e, 0xdf, 0xd3, 0x6a, 0x23, 0xdf, 0x58, + 0x42, 0x78, 0xc6, 0x3f, 0x88, 0x05, 0x89, 0xc9, 0x15, 0xfc, 0xb6, + 0x5d, 0x52, 0x3b, 0x35, 0x7c, 0x98, 0xad, 0x44, 0x2d, 0xe9, 0x94, + 0x05, 0xe8, 0x53, 0x4b, 0x56, 0x6a, 0xbf, 0x4d, 0x53, 0xc6, 0x8d, + 0x0d, 0xd3, 0x4b, 0x35, 0x71, 0x5f, 0xa1, 0x65, 0x41, 0x24, 0x88, + 0x42, 0x7c, 0x9b, 0xdf, 0x6f, 0xa1, 0xcd, 0xbd, 0x93, 0x01, 0x7d, + 0xe7, 0x48, 0x46, 0xfe, 0xa7, 0x84, 0xdc, 0x11, 0xba, 0x9b, 0x5c, + 0x1f, 0xc3, 0xeb, 0xe7, 0xcf, 0x9a, 0xe9, 0x10, 0xe9, 0xbe, 0xb2, + 0x71, 0x1e, 0x20, 0x79, 0x52, 0xf1, 0xd2, 0x1f, 0x9f, 0x8a, 0xb3, + 0x86, 0x12, 0xf6, 0x6f, 0xdd, 0x94, 0x05, 0x03, 0x92, 0x36, 0x28, + 0xa4, 0xd1, 0x57, 0x6d, 0x8b, 0x7b, 0xdb, 0x6b, 0x88, 0x45, 0x3e, + 0xb6, 0x55, 0xcf, 0xc9, 0x47, 0xbb, 0x73, 0xad, 0x1b, 0xe4, 0x03, + 0x99, 0x04, 0xca, 0xdb, 0x72, 0xe2, 0x21, 0xa3, 0x96, 0x48, 0xd0, + 0x1c, 0x1c, 0xef, 0xc2, 0x03, 0x19, 0xc1, 0xbb, 0x27, 0x7b, 0x82, + 0x98, 0xd9, 0x2a, 0xf9, 0x50, 0xe1, 0xde, 0xed, 0x3e, 0xab, 0xb9, + 0xfd, 0xa5, 0x54, 0x13, 0xc7, 0xc4, 0x7f, 0x07, 0x25, 0x9b, 0x5f, + 0x81, 0x71, 0x9b, 0xaa, 0xb0, 0x17, 0xd0, 0xde, 0x1c, 0xe7, 0x4d, + 0x82, 0xa2, 0x32, 0xee, 0x7c, 0x12, 0x7e, 0x5d, 0xa0, 0x11, 0xb1, + 0x26, 0xb7, 0x06, 0x5e, 0x5f, 0xff, 0xa6, 0x4d, 0x8e, 0xbb, 0xf1, + 0x70, 0xad, 0x94, 0xcd, 0x07, 0x88, 0xc0, 0x41, 0xb7, 0x32, 0x19, + 0x7c, 0x46, 0xb8, 0x99, 0xcd, 0xe6, 0x43, 0x98, 0x79, 0x0b, 0xae, + 0xac, 0x50, 0xce, 0x3c, 0xf4, 0x0f, 0x2d, 0x6a, 0xd8, 0x7f, 0x01, + 0xaa, 0x16, 0xe6, 0x52, 0x82, 0x7e, 0x37, 0x89, 0xe3, 0xcf, 0xea, + 0x18, 0xa1, 0xd2, 0x03, 0xc5, 0xb9, 0xcb, 0xbc, 0xbc, 0x11, 0xf6, + 0xc5, 0xf8, 0xaa, 0xd6, 0x7e, 0xb3, 0xdd, 0x94, 0x72, 0x1a, 0x50, + 0xfa, 0x39, 0x1b, 0xae, 0x40, 0x25, 0x66, 0x7e, 0xe6, 0xf2, 0xbf, + 0x9b, 0xa5, 0xf4, 0xbb, 0xf8, 0xf1, 0x9f, 0x1e, 0x5f, 0x70, 0x7b, + 0xaa, 0x37, 0x3b, 0x43, 0x51, 0xd9, 0xf2, 0x88, 0x5a, 0x6d, 0x6c, + 0x09, 0x72, 0x4e, 0xa2, 0x33, 0xe4, 0xb2, 0x7a, 0x3a, 0x6a, 0xe7, + 0x91, 0xa0, 0x4b, 0xff, 0x63, 0x6a, 0x3f, 0xdf, 0xb1, 0xa9, 0x03, + 0x69, 0x02, 0x41, 0xa5, 0x1e, 0x71, 0x9b, 0xf3, 0x1e, 0xc3, 0xf9, + 0x8f, 0xf5, 0x2e, 0x46, 0x30, 0xc1, 0xcc, 0x1f, 0x1f, 0x8c, 0x79, + 0xca, 0x40, 0xac, 0x0b, 0xe8, 0x05, 0x59, 0xe5, 0xbd, 0xbd, 0x09, + 0x62, 0x81, 0xfe, 0xfd, 0x5d, 0x4d, 0xae, 0x28, 0x3d, 0xd3, 0x5e, + 0x7d, 0x6a, 0xec, 0x7d, 0xfd, 0xb4, 0xc7, 0xda, 0xc4, 0x0b, 0xc5, + 0x91, 0x71, 0xe4, 0xcf, 0x9a, 0x95, 0xe4, 0x37, 0xc1, 0xf4, 0x58, + 0x26, 0x8e, 0xad, 0xcd, 0xce, 0xc6, 0xed, 0xb0, 0x8b, 0xd3, 0xec, + 0xa3, 0x32, 0x13, 0xd1, 0xc0, 0x5b, 0xca, 0x1e, 0x50, 0xd0, 0x95, + 0x17, 0x79, 0x0f, 0xc5, 0x79, 0x5b, 0x3f, 0xf0, 0xe1, 0x68, 0xbf, + 0xab, 0xfd, 0xc9, 0x10, 0xe5, 0xe7, 0x3e, 0x8d, 0x7a, 0xe0, 0x41, + 0x1a, 0xba, 0x88, 0xb8, 0xdc, 0xe9, 0xc7, 0x9f, 0xf6, 0x55, 0x92, + 0xaa, 0x71, 0x39, 0xa9, 0x38, 0x76, 0x7c, 0x28, 0x9d, 0x8e, 0x6a, + 0x95, 0xfa, 0xa9, 0x45, 0xa1, 0x55, 0xd1, 0xe7, 0xad, 0x6b, 0xa3, + 0x63, 0xd6, 0x47, 0x9d, 0xee, 0x8f, 0x2b, 0x1a, 0x21, 0xc1, 0xb5, + 0x3f, 0x3f, 0xa7, 0xac, 0x6a, 0xa0, 0x89, 0xeb, 0x61, 0x70, 0x7d, + 0xe0, 0x1e, 0x5d, 0x8a, 0x74, 0xbe, 0x9f, 0xbf, 0x05, 0x00, 0xf8, + 0xcf, 0xbb, 0x83, 0x15, 0x5b, 0x2d, 0x19, 0xb4, 0x38, 0x36, 0xe0, + 0xf4, 0x17, 0x1c, 0x27, 0xb7, 0xb1, 0x1a, 0xac, 0x01, 0x05, 0x8b, + 0x00, 0x9d, 0x49, 0xe4, 0x92, 0xd2, 0x9e, 0x3b, 0x24, 0xe9, 0x08, + 0x9c, 0x58, 0xcf, 0x3f, 0x37, 0xb4, 0x0b, 0xa8, 0xee, 0x06, 0x74, + 0x15, 0xa3, 0x9b, 0xa1, 0xed, 0xb5, 0x9c, 0x41, 0xf5, 0x68, 0xae, + 0xe7, 0x74, 0xb4, 0xcb, 0xd4, 0x8a, 0xc7, 0x67, 0xae, 0xa6, 0x7a, + 0xe8, 0xab, 0x5d, 0x94, 0x0f, 0x34, 0x5d, 0xfe, 0xeb, 0x01, 0x13, + 0xac, 0xec, 0xc0, 0xb3, 0x05, 0x46, 0x19, 0xfd, 0x40, 0xdc, 0x34, + 0x00, 0xee, 0xf7, 0xcd, 0xd3, 0xfb, 0x48, 0xa9, 0xdc, 0xa7, 0xba, + 0x5d, 0x2b, 0x4c, 0x63, 0x7f, 0xb6, 0x2d, 0xfc, 0xd0, 0xdd, 0xf3, + 0x94, 0x2c, 0x82, 0xe2, 0xa9, 0x46, 0xd2, 0x80, 0x8e, 0x64, 0x13, + 0x23, 0x34, 0x1b, 0xbd, 0xca, 0xc2, 0xde, 0xab, 0x56, 0x73, 0xb0, + 0xcf, 0xcd, 0x57, 0xef, 0x0f, 0x7d, 0x55, 0x8d, 0x65, 0xcf, 0x36, + 0xbc, 0x0a, 0xe2, 0xcd, 0xdd, 0x7a, 0xa2, 0xba, 0x48, 0x94, 0xe7, + 0x65, 0xdd, 0x77, 0x7c, 0x80, 0xd3, 0x43, 0x71, 0x8f, 0x4c, 0x7f, + 0x62, 0x34, 0x06, 0xae, 0x1f, 0xe9, 0x18, 0x8e, 0x7a, 0xc7, 0x9c, + 0x46, 0x30, 0xfd, 0xca, 0xbd, 0x6f, 0x48, 0x47, 0xfa, 0x40, 0xc1, + 0xbb, 0x08, 0x06, 0x83, 0xa7, 0x01, 0xd8, 0x57, 0x33, 0x41, 0x28, + 0xfe, 0x5b, 0x9d, 0x94, 0xd3, 0x7c, 0x69, 0xe5, 0xf1, 0x71, 0x13, + 0x9f, 0x78, 0x33, 0xe9, 0x20, 0xf9, 0x8a, 0xbb, 0x04, 0xf5, 0x12, + 0xa9, 0xbf, 0x05, 0x66, 0x82, 0x7f, 0xb6, 0xc4, 0xbe, 0x74, 0x02, + 0x0f, 0xd0, 0x72, 0x75, 0xd8, 0x4b, 0x96, 0x34, 0xcb, 0x3f, 0x84, + 0x97, 0xa9, 0xfc, 0xde, 0x6c, 0x91, 0x91, 0x60, 0x19, 0x9e, 0x5f, + 0x12, 0xff, 0x67, 0x62, 0xe4, 0x11, 0xd0, 0x33, 0xdd, 0x88, 0xd4, + 0x10, 0x17, 0x70, 0x62, 0xfe, 0x09, 0xba, 0x1e, 0x70, 0x14, 0x9c, + 0x3d, 0xdd, 0xfa, 0xd7, 0x1a, 0x8a, 0x67, 0x52, 0xbd, 0xf3, 0xac, + 0xe3, 0x60, 0x54, 0xb4, 0x44, 0xff, 0xe8, 0xab, 0x2d, 0x88, 0xdf, + 0xde, 0x69, 0xe2, 0x90, 0x53, 0x7d, 0x6a, 0xee, 0xeb, 0xf9, 0xed, + 0x72, 0xd4, 0x56, 0xcc, 0x7f, 0x0a, 0x4a, 0xd6, 0x2d, 0x32, 0xd6, + 0x3f, 0xd4, 0xfd, 0x91, 0x8d, 0x6c, 0xb3, 0xff, 0xbe, 0x83, 0xa6, + 0x27, 0xe0, 0x2e, 0xb9, 0x5f, 0x1c, 0xc3, 0x7b, 0x36, 0xb7, 0xfb, + 0x56, 0x38, 0x87, 0xb0, 0x5a, 0x6f, 0x7c, 0x76, 0x36, 0xc1, 0xfd, + 0x7c, 0x6c, 0x17, 0xa1, 0x36, 0x75, 0x7a, 0x58, 0x54, 0x54, 0x01, + 0x4e, 0xb5, 0xe9, 0xb2, 0x49, 0xaa, 0xaa, 0xfe, 0x22, 0x31, 0xf6, + 0xb6, 0x93, 0x3e, 0xfe, 0x6b, 0x72, 0xf8, 0x02, 0x20, 0x7e, 0x82, + 0xdd, 0x63, 0x93, 0x04, 0x35, 0xca, 0x48, 0xae, 0x11, 0xa3, 0x4e, + 0x4c, 0x3a, 0x98, 0x9b, 0x9a, 0x82, 0x07, 0x06, 0x18, 0xec, 0x87, + 0xde, 0x91, 0x47, 0xd5, 0x5c, 0xa7, 0x67, 0xc1, 0xb7, 0xd2, 0xcc, + 0xcc, 0x13, 0x3e, 0x15, 0xa2, 0x22, 0x85, 0x2a, 0x7f, 0xfa, 0x34, + 0x6b, 0xa3, 0xfb, 0xc7, 0xfc, 0xf3, 0x73, 0x20, 0x0d, 0x4f, 0xfd, + 0x35, 0x2f, 0x39, 0x56, 0x20, 0xb4, 0xbe, 0x98, 0xff, 0xea, 0x12, + 0x8f, 0x92, 0xfa, 0xdf, 0xb4, 0xbf, 0xdb, 0x19, 0x03, 0xe0, 0x55, + 0x3a, 0x10, 0x56, 0x19, 0x9a, 0x63, 0x36, 0x56, 0x92, 0x13, 0x4e, + 0x76, 0x5c, 0x56, 0x0d, 0xf6, 0xa3, 0x69, 0x99, 0xc9, 0xcb, 0xa6, + 0xf5, 0xcc, 0xb7, 0x15, 0xbf, 0x91, 0x4e, 0x4a, 0xf1, 0x7b, 0x55, + 0x7c, 0xb7, 0xef, 0x48, 0xe5, 0xc9, 0x77, 0xcd, 0x99, 0x69, 0xb0, + 0x77, 0x6d, 0x22, 0xda, 0xf8, 0x2d, 0xae, 0x82, 0xc1, 0x29, 0x68, + 0xfb, 0x36, 0xf3, 0x40, 0x78, 0x52, 0x91, 0x61, 0x2c, 0xe2, 0xdc, + 0xa7, 0xbb, 0x9d, 0xa6, 0x4f, 0xa5, 0x3f, 0x42, 0x28, 0x59, 0x7e, + 0xe8, 0x97, 0x54, 0xa8, 0xa2, 0xa5, 0x8b, 0xdb, 0x25, 0xf2, 0xa7, + 0xc9, 0xef, 0xf7, 0x0e, 0x28, 0xfc, 0x3d, 0xd3, 0xf1, 0x5e, 0x30, + 0x7f, 0x66, 0xfc, 0x07, 0xce, 0x94, 0xe7, 0xaf, 0x4b, 0x38, 0x80, + 0x83, 0x61, 0xe4, 0x86, 0xa1, 0xe7, 0x53, 0xba, 0xa1, 0x46, 0x12, + 0xb6, 0xc7, 0xe7, 0xc0, 0x13, 0x8e, 0x8f, 0x85, 0x7b, 0xd7, 0x40, + 0xe2, 0x64, 0x87, 0x66, 0xa9, 0x4c, 0x0f, 0x8e, 0xb0, 0x27, 0x85, + 0x44, 0x47, 0xda, 0x14, 0x07, 0xe6, 0xb5, 0x44, 0x09, 0x09, 0xc7, + 0xe9, 0xe5, 0x2e, 0xad, 0x14, 0xf4, 0xa6, 0xbb, 0x2c, 0xff, 0xc2, + 0x92, 0xdc, 0xf3, 0x0f, 0x30, 0xac, 0x76, 0xf5, 0xf1, 0x4f, 0x13, + 0x6e, 0x7d, 0x73, 0xd5, 0x76, 0x81, 0xa5, 0x86, 0x9b, 0xcf, 0x51, + 0xfb, 0x80, 0xa5, 0x80, 0x8d, 0xa2, 0x72, 0x5f, 0x58, 0x89, 0xb8, + 0x3c, 0xe4, 0xa5, 0x9a, 0xae, 0x5b, 0xad, 0xe8, 0xf2, 0x94, 0x00, + 0x40, 0x75, 0xba, 0xe2, 0x07, 0xda, 0xc1, 0xf0, 0xf8, 0xb4, 0xb2, + 0x15, 0x71, 0x7d, 0xd4, 0xbc, 0xda, 0xbb, 0xb5, 0xca, 0xfd, 0xd7, + 0x3b, 0x3e, 0xe9, 0x91, 0xad, 0xdb, 0x79, 0x45, 0xd8, 0xb0, 0x66, + 0x6a, 0xd1, 0xca, 0xc7, 0xf1, 0x2c, 0x0d, 0x1b, 0xa1, 0xa1, 0xfb, + 0xc3, 0x9e, 0x2f, 0x32, 0x80, 0xd1, 0x5a, 0xf5, 0x7d, 0x41, 0x1c, + 0x97, 0x38, 0xa7, 0xbc, 0xdc, 0xfc, 0xbf, 0x72, 0x7c, 0x0c, 0xfe, + 0xc5, 0x09, 0x92, 0x6b, 0x4c, 0x2c, 0x87, 0x42, 0xdd, 0x71, 0x0d, + 0xb6, 0x2e, 0xff, 0x31, 0xbd, 0xff, 0x58, 0xbd, 0xa0, 0xc2, 0xea, + 0x74, 0x65, 0xa8, 0xa1, 0x99, 0x4f, 0xc5, 0x9f, 0x51, 0xac, 0xfb, + 0x9d, 0xe2, 0x8f, 0x00, 0xf2, 0xa2, 0xab, 0x95, 0x84, 0xbc, 0x98, + 0xfc, 0x30, 0xde, 0x5f, 0xc7, 0x4b, 0xc3, 0x2c, 0x07, 0x39, 0x84, + 0xcb, 0xdc, 0x47, 0xfb, 0xe3, 0xb3, 0xd8, 0x8b, 0x6b, 0x65, 0x3f, + 0xb3, 0xf0, 0x14, 0xd5, 0xc5, 0x94, 0x62, 0xbe, 0x4f, 0x29, 0xaf, + 0x4a, 0x7b, 0x0e, 0x0e, 0x25, 0xef, 0xf1, 0x09, 0xbf, 0x7d, 0x02, + 0x95, 0x79, 0x60, 0x21, 0x85, 0xfa, 0xdf, 0xe8, 0x38, 0x32, 0x88, + 0x3f, 0x0a, 0xbe, 0x5e, 0x96, 0x52, 0x00, 0xab, 0x85, 0x03, 0x0b, + 0x43, 0xc5, 0x35, 0x75, 0xa8, 0x9d, 0x55, 0x91, 0xa9, 0x97, 0x85, + 0xfe, 0x44, 0x45, 0xeb, 0xa9, 0x42, 0xec, 0xae, 0x81, 0xb1, 0x94, + 0xcb, 0xfd, 0x7f, 0xf3, 0xb7, 0x45, 0x1e, 0xe7, 0xcf, 0xdd, 0x20, + 0x06, 0x26, 0xc7, 0x93, 0x82, 0x1e, 0x86, 0x84, 0xa7, 0xe7, 0x62, + 0xce, 0x09, 0xd6, 0xd6, 0x9b, 0xde, 0x52, 0xfb, 0x90, 0x3f, 0xb1, + 0x5c, 0x5a, 0x39, 0x5b, 0xcb, 0xd0, 0xc0, 0x6f, 0x27, 0x3b, 0x08, + 0xb4, 0x33, 0xfe, 0xf8, 0x07, 0xff, 0x86, 0xb1, 0x74, 0x1f, 0x89, + 0xfa, 0x36, 0x57, 0xd2, 0x5f, 0xe8, 0xac, 0x68, 0xa8, 0x15, 0xa4, + 0x9a, 0x76, 0xac, 0x44, 0xf6, 0xbe, 0xd6, 0x67, 0x66, 0x0b, 0xc9, + 0xf0, 0x19, 0xec, 0x44, 0x5d, 0xc7, 0x5d, 0x3e, 0xad, 0xdd, 0x73, + 0x99, 0x64, 0x78, 0x2c, 0x7e, 0x14, 0xf9, 0x70, 0x5c, 0xd3, 0xd9, + 0x15, 0x17, 0x11, 0x17, 0x14, 0x35, 0x9b, 0xb9, 0xa5, 0x6b, 0x6c, + 0x2f, 0x37, 0x8f, 0xfd, 0xfa, 0xe5, 0xfd, 0xd4, 0x74, 0x0f, 0xaa, + 0x65, 0xfe, 0xf8, 0xf3, 0x4c, 0x45, 0x71, 0x75, 0x31, 0xa3, 0x02, + 0x1b, 0x65, 0xbe, 0x7d, 0xa2, 0x09, 0xb1, 0xcc, 0x85, 0xe2, 0x39, + 0x8a, 0x5a, 0x29, 0xb9, 0x9a, 0x03, 0x42, 0xbb, 0x9d, 0x1a, 0xcf, + 0xd1, 0x1c, 0x26, 0x81, 0x28, 0xe5, 0x47, 0x1c, 0xfe, 0x6e, 0xeb, + 0xfc, 0xb3, 0x75, 0xa5, 0x52, 0x3e, 0x8c, 0x01, 0x04, 0x20, 0x70, + 0xcf, 0x94, 0x56, 0x22, 0xdf, 0x0c, 0x0f, 0xbd, 0x71, 0x30, 0x3c, + 0xe4, 0x4d, 0xc5, 0xbb, 0x8b, 0x5b, 0xe7, 0x48, 0xd9, 0x52, 0xc5, + 0x9e, 0x73, 0xaf, 0x4e, 0x04, 0xbe, 0xc2, 0xc7, 0x59, 0x96, 0xbe, + 0x74, 0xe8, 0x6a, 0xb9, 0x26, 0xc2, 0x86, 0xf9, 0x9e, 0x2e, 0x51, + 0xa4, 0x5a, 0x5a, 0x39, 0xc2, 0x7b, 0x16, 0x37, 0xcb, 0x49, 0x11, + 0x76, 0xe7, 0x52, 0x9a, 0x89, 0x5f, 0x0c, 0x32, 0x12, 0x8e, 0x87, + 0x54, 0x71, 0x32, 0x3e, 0xe7, 0x07, 0x8b, 0x87, 0x54, 0x50, 0x81, + 0xc7, 0x91, 0x29, 0x1f, 0x43, 0x17, 0xbe, 0xbd, 0x3b, 0x43, 0xa2, + 0xb1, 0x55, 0x76, 0xc2, 0x87, 0xbe, 0x34, 0xcc, 0x5d, 0xbb, 0xa8, + 0x2e, 0x8b, 0xee, 0xfd, 0xdc, 0x71, 0xd6, 0x89, 0xa0, 0xa7, 0x03, + 0x44, 0x1e, 0x1e, 0x64, 0x28, 0x8e, 0xc9, 0xd0, 0x86, 0xa9, 0x78, + 0x74, 0xf4, 0x18, 0x9c, 0x10, 0x9d, 0xcb, 0x27, 0xbd, 0xa3, 0xe9, + 0x8a, 0xdd, 0xfc, 0x0b, 0x7c, 0x2f, 0x8e, 0x0c, 0x49, 0x15, 0x54, + 0x14, 0x33, 0x78, 0x87, 0x7b, 0xeb, 0x38, 0xc1, 0x30, 0xf0, 0xb8, + 0x90, 0x72, 0x41, 0x72, 0xe4, 0x3c, 0xe5, 0x40, 0x15, 0xf0, 0x1f, + 0xf7, 0xaf, 0x26, 0xc5, 0x6d, 0x27, 0xcd, 0x9e, 0x74, 0x7b, 0x90, + 0xec, 0x1e, 0xcb, 0xfd, 0x8d, 0x56, 0x98, 0x0a, 0x44, 0x85, 0x08, + 0xf9, 0xfd, 0x38, 0x7d, 0x12, 0xfb, 0x94, 0x88, 0x78, 0x60, 0xd2, + 0x0d, 0xa5, 0x86, 0x33, 0x47, 0x8f, 0xa7, 0xc5, 0xaf, 0x73, 0xe6, + 0xb7, 0x96, 0x02, 0xce, 0x79, 0x64, 0xf8, 0xf2, 0x29, 0x26, 0xbe, + 0x08, 0xea, 0xcc, 0x0d, 0x11, 0x98, 0xc7, 0x49, 0x3a, 0xbe, 0xe2, + 0xe0, 0x9d, 0x85, 0x85, 0x3a, 0xe7, 0x59, 0x2e, 0xa6, 0x9d, 0x8f, + 0xa7, 0x9e, 0x13, 0x5c, 0xa2, 0x8b, 0x9a, 0xf5, 0x52, 0x9a, 0xbe, + 0xc8, 0x8f, 0xf6, 0xb1, 0xe9, 0x6f, 0x30, 0xc1, 0xf3, 0x2d, 0x4f, + 0x59, 0x7e, 0xc7, 0x45, 0x0a, 0x7f, 0x60, 0x04, 0x0d, 0xb8, 0x36, + 0xa1, 0xbf, 0xb2, 0x2e, 0x5b, 0x4d, 0x17, 0xe2, 0xac, 0x46, 0x62, + 0xc3, 0xa7, 0xe3, 0x16, 0x4a, 0x7f, 0x84, 0xcb, 0xab, 0x2c, 0xa6, + 0x01, 0xd5, 0x67, 0x3d, 0xea, 0x46, 0xf9, 0x84, 0x28, 0xcd, 0xe9, + 0xc9, 0xcc, 0xef, 0x6c, 0x92, 0xc1, 0x8a, 0x3d, 0x8f, 0xd5, 0x90, + 0x91, 0x4b, 0x98, 0x33, 0x6b, 0xe9, 0xd7, 0x67, 0xd2, 0xdb, 0xc0, + 0xa7, 0x99, 0x4a, 0xce, 0x66, 0xed, 0x72, 0xaf, 0x78, 0xaf, 0x69, + 0xd7, 0x31, 0x93, 0x7a, 0x99, 0xfd, 0xaf, 0x24, 0xf5, 0xd9, 0x6b, + 0x0e, 0x72, 0x11, 0xfa, 0x88, 0x43, 0x3f, 0x8d, 0x63, 0xfe, 0x3a, + 0xb4, 0xa7, 0xa6, 0x65, 0x6e, 0x5e, 0x96, 0xaa, 0x2f, 0xf6, 0x4b, + 0xdb, 0x35, 0x0a, 0xf1, 0x53, 0xae, 0xaa, 0xb4, 0x89, 0x8f, 0xf4, + 0x66, 0xdc, 0x7f, 0xae, 0xd4, 0x3e, 0x3c, 0x8c, 0x7a, 0x2f, 0x6a, + 0xce, 0x25, 0xea, 0x36, 0x13, 0x50, 0x99, 0x30, 0x34, 0x35, 0xcd, + 0xbf, 0xf4, 0xb4, 0xf2, 0x81, 0x8e, 0x7b, 0x30, 0xc8, 0xf1, 0x8c, + 0x2c, 0x70, 0xfb, 0x35, 0x2e, 0x9e, 0x55, 0x5b, 0x40, 0x4f, 0x64, + 0xe0, 0xf5, 0xe7, 0xb1, 0xde, 0x8c, 0x9b, 0xff, 0xf9, 0x1c, 0xbf, + 0x28, 0xf4, 0x4d, 0xc5, 0xb8, 0x75, 0x14, 0xc3, 0xdb, 0xec, 0x17, + 0x5c, 0x7a, 0x60, 0x89, 0xbe, 0xfb, 0x21, 0x11, 0xc0, 0x31, 0x35, + 0x57, 0x2c, 0x85, 0xb2, 0xf7, 0xea, 0x8a, 0xa1, 0xf9, 0x3c, 0x7c, + 0x71, 0x6b, 0xf5, 0xa1, 0x72, 0xe1, 0xcb, 0x73, 0x82, 0xd7, 0xc6, + 0x09, 0x8c, 0xe3, 0x5f, 0x1a, 0xd4, 0xb9, 0xca, 0xaf, 0x00, 0x4a, + 0xf7, 0xe2, 0xbb, 0xf6, 0x87, 0x57, 0xa5, 0x16, 0x77, 0xd8, 0x45, + 0x62, 0x76, 0xfa, 0xdf, 0x6b, 0xe5, 0x3b, 0x38, 0x23, 0x31, 0xc7, + 0x47, 0x59, 0x1e, 0xeb, 0xa0, 0xfb, 0xc0, 0x4b, 0x13, 0x3b, 0xc5, + 0xd8, 0x10, 0x3a, 0x05, 0x54, 0xa9, 0x60, 0xbd, 0xf8, 0xee, 0x25, + 0x0e, 0x48, 0x9a, 0x2e, 0xd7, 0xc4, 0xc2, 0xef, 0x7c, 0xff, 0x6b, + 0x82, 0x48, 0xb5, 0xc8, 0x2c, 0x7e, 0x82, 0xb8, 0x84, 0xec, 0x5d, + 0x52, 0xd9, 0xa6, 0x4f, 0x20, 0xc3, 0xcf, 0x76, 0x16, 0x69, 0xb7, + 0xe0, 0x04, 0xba, 0xb2, 0xcf, 0xbf, 0xac, 0xdf, 0xbc, 0xf2, 0x8e, + 0x4d, 0xba, 0x79, 0xc8, 0x0f, 0x7a, 0xbb, 0x54, 0x8d, 0x4f, 0x5a, + 0x60, 0x16, 0x8d, 0xfd, 0x70, 0x6f, 0x8d, 0x25, 0x34, 0x33, 0xcd, + 0x10, 0x30, 0x77, 0xf3, 0x14, 0x1a, 0xeb, 0x69, 0x42, 0xc4, 0x7d, + 0x9b, 0x4b, 0xbd, 0x7e, 0x39, 0xc2, 0xf9, 0xc3, 0x6c, 0x0e, 0xca, + 0x4c, 0xc3, 0x0e, 0xa4, 0x9b, 0x7f, 0xb7, 0x76, 0x83, 0xf3, 0xac, + 0x7c, 0x2c, 0xf7, 0xbc, 0x68, 0x42, 0x8e, 0xcf, 0x08, 0x65, 0x89, + 0x08, 0xf2, 0xdf, 0x5b, 0x56, 0xf8, 0xf2, 0x53, 0x20, 0xe0, 0xd0, + 0x78, 0xdd, 0xd3, 0x74, 0x9c, 0x4a, 0x98, 0x47, 0xb0, 0xbf, 0x99, + 0xc4, 0x0e, 0x3c, 0xf9, 0xf7, 0x9c, 0xd2, 0xda, 0xe2, 0x58, 0xed, + 0xd8, 0xc2, 0xd4, 0xb2, 0xcb, 0x37, 0x5b, 0xc2, 0xc6, 0xfa, 0x78, + 0xac, 0x72, 0xfe, 0x68, 0x15, 0x96, 0xcd, 0xef, 0x7c, 0x36, 0x6e, + 0xf9, 0x89, 0x9a, 0x44, 0x10, 0xfa, 0xf7, 0xb4, 0xff, 0x0c, 0x27, + 0x4f, 0xb3, 0x04, 0x78, 0x6c, 0x40, 0x63, 0xcc, 0x46, 0x86, 0x1b, + 0xf6, 0xf8, 0x8f, 0x6d, 0xa5, 0xdf, 0xcf, 0x90, 0xda, 0x14, 0x36, + 0x7c, 0x5b, 0xa5, 0xe3, 0xbc, 0x76, 0xf1, 0x66, 0x90, 0xb5, 0x98, + 0x4c, 0x3c, 0x28, 0xdd, 0xd5, 0xf8, 0x59, 0x85, 0x1f, 0x8b, 0xe6, + 0x8a, 0x50, 0xb1, 0xf4, 0x2c, 0x80, 0xff, 0x67, 0x6e, 0x8a, 0xb6, + 0x49, 0x6c, 0x82, 0x31, 0xb0, 0x07, 0xa3, 0xba, 0x1f, 0xad, 0xb8, + 0xf3, 0xda, 0xf7, 0xbe, 0xb7, 0x95, 0x5d, 0x20, 0x94, 0x1f, 0xa7, + 0xe7, 0x2d, 0xdd, 0xfe, 0x5a, 0x15, 0x66, 0xd9, 0x3c, 0xfa, 0xa3, + 0xff, 0xcf, 0x2f, 0xb5, 0x60, 0xd5, 0xd9, 0x0b, 0xbe, 0x45, 0x84, + 0x87, 0x80, 0xc0, 0x7b, 0x63, 0x2d, 0xe4, 0xd8, 0xb7, 0x0b, 0x48, + 0x0f, 0xbc, 0x5c, 0x13, 0x42, 0xda, 0xd4, 0xfe, 0x87, 0x6c, 0x31, + 0x49, 0x42, 0x62, 0x08, 0xb8, 0xa8, 0xb9, 0x48, 0x01, 0x36, 0x6f, + 0x7a, 0x9c, 0x42, 0x21, 0xb6, 0xcc, 0xa0, 0xa1, 0xde, 0x26, 0x36, + 0xde, 0xf2, 0x60, 0xe2, 0x28, 0xf3, 0x94, 0x4a, 0xec, 0x19, 0x4e, + 0x72, 0xdb, 0x9f, 0xc1, 0xbd, 0xce, 0xa4, 0xa2, 0xf6, 0x6a, 0x3c, + 0xca, 0x76, 0xb2, 0x59, 0xfe, 0xda, 0x4c, 0x06, 0x87, 0x0c, 0x07, + 0x9b, 0x67, 0x44, 0xae, 0x74, 0x98, 0xb8, 0xe6, 0x08, 0xc1, 0x93, + 0x95, 0x35, 0x15, 0x2a, 0x57, 0x53, 0x44, 0xad, 0xb7, 0x2f, 0x46, + 0x0f, 0x32, 0x9d, 0xc8, 0x5b, 0xfc, 0x1e, 0xce, 0x06, 0x52, 0x94, + 0xdf, 0x57, 0x44, 0xc8, 0x0c, 0x1c, 0xb3, 0x38, 0x73, 0x46, 0x08, + 0xea, 0x11, 0x8e, 0xc4, 0x5e, 0x67, 0xd0, 0x19, 0xbf, 0xd0, 0x75, + 0xb3, 0x3f, 0x06, 0xe3, 0xbc, 0xd8, 0xb3, 0xce, 0x9b, 0x75, 0x3a, + 0x7f, 0xc7, 0x79, 0xa9, 0x50, 0x43, 0x00, 0x53, 0xc4, 0xdd, 0x33, + 0xa3, 0x84, 0x8d, 0x2b, 0x4f, 0x72, 0x09, 0xcf, 0xa7, 0x6b, 0x30, + 0x4e, 0x2e, 0x72, 0x68, 0xeb, 0x8e, 0x90, 0x44, 0x25, 0xc9, 0xf4, + 0xb0, 0x00, 0x29, 0xe3, 0x64, 0x98, 0x93, 0x58, 0x7f, 0xe4, 0xa4, + 0x9b, 0x5a, 0x7d, 0x89, 0x6d, 0xee, 0xd8, 0x76, 0x3c, 0xf2, 0x48, + 0xd8, 0xef, 0xba, 0x37, 0x02, 0x54, 0x89, 0xfb, 0x87, 0xf9, 0xae, + 0xed, 0x6c, 0x4c, 0x40, 0x0d, 0xab, 0x73, 0x75, 0xed, 0xd9, 0xf1, + 0x56, 0xbb, 0xdc, 0xc0, 0x71, 0x80, 0xde, 0xe3, 0xd9, 0x1e, 0xe6, + 0x16, 0xac, 0x71, 0xfd, 0x8a, 0x65, 0x64, 0xc7, 0x4f, 0x40, 0x3f, + 0xae, 0xaf, 0x7c, 0x1b, 0x7a, 0x21, 0xcd, 0x87, 0x12, 0xe8, 0xd7, + 0xbe, 0x79, 0xbc, 0xaf, 0x97, 0x69, 0x43, 0x7f, 0x04, 0x8c, 0xd8, + 0x42, 0xd2, 0xb9, 0x06, 0x8d, 0x7e, 0x57, 0x67, 0x56, 0xb7, 0x19, + 0xf7, 0x42, 0xc4, 0xae, 0xee, 0x1d, 0xed, 0xd4, 0xce, 0x22, 0x2e, + 0x3a, 0x35, 0xbe, 0xac, 0xf0, 0xcc, 0x77, 0xea, 0xcc, 0x06, 0x9f, + 0xaa, 0x73, 0x90, 0xac, 0xc2, 0xb7, 0xca, 0xfb, 0xb6, 0xdf, 0xff, + 0x58, 0x9d, 0x97, 0x2b, 0x10, 0x47, 0xcb, 0x21, 0x61, 0xb4, 0xef, + 0xba, 0xb0, 0xcb, 0x54, 0x53, 0x1e, 0x22, 0xe1, 0x8d, 0xc2, 0xba, + 0x05, 0x4a, 0x01, 0x31, 0x3b, 0xad, 0xc0, 0x49, 0xfd, 0x7c, 0xc5, + 0xe7, 0xe2, 0x24, 0xbc, 0xe0, 0xdd, 0x01, 0x06, 0xde, 0xf6, 0xde, + 0xad, 0x19, 0xee, 0xc0, 0xee, 0x38, 0x09, 0x5a, 0x09, 0x2e, 0x42, + 0x3a, 0x52, 0xd4, 0x0d, 0x1c, 0xc4, 0x0b, 0x28, 0xe8, 0x0c, 0xe1, + 0x8d, 0xca, 0xac, 0x0d, 0x2f, 0x79, 0xaa, 0x15, 0x28, 0xa8, 0x47, + 0x4e, 0x2c, 0x65, 0x68, 0xf8, 0xdf, 0x83, 0x6f, 0x72, 0x69, 0x8c, + 0x81, 0x8d, 0x18, 0x16, 0xfb, 0xce, 0x46, 0xf4, 0xd3, 0xf4, 0xef, + 0x0f, 0x88, 0x0e, 0x0d, 0xb9, 0x31, 0x9c, 0xa6, 0x18, 0x0c, 0x1b, + 0x02, 0x6a, 0x28, 0xed, 0xd8, 0xfa, 0x36, 0x3f, 0xff, 0xa9, 0x87, + 0xfa, 0x7f, 0xd8, 0x07, 0xba, 0x73, 0xcf, 0xaf, 0xd4, 0x5e, 0x5a, + 0xb7, 0xeb, 0xa8, 0x0d, 0x27, 0xfd, 0xed, 0x97, 0x54, 0x2f, 0x45, + 0x7c, 0xe4, 0xd8, 0xd7, 0x96, 0xc7, 0xc9, 0x75, 0x5c, 0x40, 0x3b, + 0x64, 0xa7, 0x96, 0xfc, 0x0e, 0x63, 0xf9, 0x20, 0xfa, 0x68, 0x4b, + 0x78, 0x4b, 0x2a, 0xea, 0x63, 0xf5, 0x4d, 0x84, 0x56, 0x2e, 0xbb, + 0x84, 0x7d, 0x6a, 0x38, 0xbe, 0xd8, 0x1a, 0x49, 0x08, 0xfb, 0xd4, + 0xde, 0x17, 0xc4, 0xfd, 0x0e, 0x27, 0x3c, 0x84, 0x7e, 0x68, 0x5d, + 0xf1, 0x7c, 0x69, 0x0a, 0xe8, 0xfc, 0x1c, 0xdd, 0xfc, 0x6b, 0x8b, + 0x10, 0xe7, 0x44, 0x64, 0x40, 0x7c, 0xfc, 0x59, 0x79, 0x3f, 0x2f, + 0xa7, 0x68, 0xe4, 0x24, 0x91, 0x29, 0x9b, 0x74, 0x99, 0x25, 0xf1, + 0xcf, 0xac, 0xf5, 0x1e, 0x29, 0x95, 0x9d, 0x0f, 0x16, 0x78, 0x1f, + 0xf7, 0xa7, 0x7a, 0x6b, 0xdf, 0xf8, 0x1f, 0xca, 0xfc, 0x95, 0x88, + 0x35, 0x65, 0xf0, 0x8f, 0x47, 0x9a, 0xc7, 0x14, 0x27, 0x17, 0x98, + 0xdf, 0xba, 0x44, 0xa3, 0xfe, 0x6d, 0x01, 0xa9, 0x11, 0xe7, 0x6f, + 0xed, 0x73, 0x7d, 0x14, 0x35, 0x9d, 0x61, 0xa5, 0xc6, 0xee, 0x00, + 0x7f, 0x41, 0x13, 0x4b, 0x3f, 0xef, 0xb8, 0x3a, 0x9a, 0xbe, 0x35, + 0xc7, 0x75, 0x58, 0xe8, 0x7d, 0xe7, 0xd4, 0x2d, 0xba, 0xe6, 0x05, + 0x67, 0xe5, 0x4e, 0x09, 0x16, 0xc5, 0xc7, 0x2b, 0xd1, 0x9b, 0x1b, + 0x5e, 0x91, 0x30, 0xe3, 0x56, 0xc3, 0x83, 0xc8, 0xc7, 0x79, 0xde, + 0xbc, 0x74, 0xe4, 0x44, 0x37, 0xe3, 0x0e, 0x0c, 0xc6, 0xeb, 0x67, + 0xf4, 0x45, 0x11, 0x67, 0x2c, 0xe7, 0x9f, 0x24, 0xe7, 0x7d, 0x51, + 0x37, 0x92, 0x90, 0x5e, 0x23, 0x4d, 0x1e, 0x19, 0x84, 0x89, 0x00, + 0x9c, 0xa9, 0x6e, 0x2c, 0x46, 0x9f, 0x63, 0x8f, 0x2d, 0xf1, 0xdb, + 0x9c, 0x6e, 0x24, 0x09, 0xb1, 0x14, 0x06, 0xca, 0xab, 0xcf, 0xe3, + 0xfb, 0xce, 0x94, 0x11, 0xb9, 0x55, 0xf9, 0x11, 0x52, 0x13, 0x13, + 0x92, 0x61, 0xf8, 0xa8, 0xc8, 0x21, 0x36, 0x62, 0x34, 0xd8, 0xd8, + 0xb7, 0x6d, 0x51, 0xf3, 0x5c, 0x68, 0xe1, 0x52, 0x16, 0xda, 0xc9, + 0xef, 0x29, 0x3e, 0xc4, 0x64, 0x4a, 0x06, 0xd4, 0x06, 0x7f, 0x8b, + 0x21, 0x46, 0xdf, 0x8a, 0x7b, 0x9c, 0xee, 0x13, 0x6b, 0x88, 0xff, + 0x46, 0x3c, 0x7d, 0x11, 0xce, 0x4b, 0x39, 0x21, 0xeb, 0x66, 0x7f, + 0x92, 0x1e, 0x63, 0x2e, 0x60, 0xe3, 0x42, 0x65, 0x5e, 0xb5, 0xbb, + 0xa0, 0xc4, 0x10, 0x3f, 0xb5, 0xb7, 0x6a, 0xcd, 0x3d, 0x49, 0x43, + 0x49, 0x7d, 0xb2, 0x44, 0xe9, 0xee, 0xdc, 0xa2, 0x87, 0xb8, 0xa0, + 0x09, 0x68, 0x4c, 0x4f, 0x17, 0x72, 0xf4, 0x01, 0x64, 0xb8, 0x5a, + 0xfe, 0x04, 0x27, 0x83, 0x49, 0xac, 0xad, 0xd3, 0xa9, 0x18, 0xbf, + 0x72, 0xee, 0x0e, 0xe0, 0xf9, 0x5c, 0xa5, 0x8a, 0x94, 0x32, 0x21, + 0x9c, 0xa2, 0x78, 0x2a, 0x6e, 0x48, 0x47, 0x4f, 0xb7, 0x5f, 0x8c, + 0xc4, 0x81, 0x68, 0x18, 0x3d, 0x54, 0x58, 0x8b, 0x0f, 0xba, 0x03, + 0x19, 0xc7, 0x38, 0x2d, 0xe4, 0xff, 0xf8, 0x0a, 0xed, 0xe5, 0x30, + 0x85, 0x7e, 0xe6, 0x67, 0x12, 0x67, 0x5d, 0x7a, 0xe8, 0xb2, 0x10, + 0x6f, 0xd4, 0xef, 0x1c, 0x21, 0xd0, 0x5c, 0xad, 0x05, 0x7f, 0x9d, + 0x38, 0x0f, 0x9a, 0xa2, 0x88, 0xfc, 0x48, 0xa7, 0x3c, 0x35, 0xee, + 0xf1, 0xe7, 0x79, 0x7f, 0xd2, 0xd0, 0x0a, 0xfa, 0xa9, 0x96, 0xfb, + 0x73, 0x00, 0xe7, 0x92, 0x37, 0x55, 0x0f, 0xbc, 0x70, 0x41, 0xbd, + 0x41, 0x8b, 0xd3, 0x5f, 0xc3, 0x31, 0x83, 0x1b, 0xd0, 0x5a, 0x3c, + 0xcc, 0xb5, 0xeb, 0xe8, 0x4e, 0xdc, 0xe5, 0xf5, 0xfd, 0x1d, 0xa4, + 0x2d, 0x5a, 0x87, 0xc1, 0xb8, 0xdf, 0x49, 0x14, 0x3a, 0x12, 0x2b, + 0x04, 0x40, 0xb3, 0xd7, 0x62, 0x6e, 0xd8, 0x87, 0x69, 0x1d, 0x8b, + 0x3f, 0x94, 0xd1, 0x0f, 0x60, 0xf8, 0x70, 0x8b, 0x0b, 0x8c, 0xdd, + 0x70, 0x46, 0x8d, 0x2f, 0x2e, 0x4d, 0xad, 0x23, 0xaf, 0xa0, 0x44, + 0xdd, 0x33, 0x70, 0x75, 0x3d, 0x88, 0x4e, 0xae, 0x46, 0x24, 0xac, + 0xe7, 0x20, 0xd4, 0x03, 0x4b, 0x11, 0xd3, 0x20, 0x2a, 0x82, 0x87, + 0xe2, 0xe3, 0x76, 0x85, 0x76, 0x69, 0xba, 0x7a, 0x7f, 0x20, 0xe9, + 0xaa, 0xfd, 0xf6, 0xba, 0x4b, 0x31, 0xc8, 0x81, 0x41, 0x12, 0xf6, + 0x78, 0x0b, 0xe3, 0x69, 0xfc, 0x6c, 0x28, 0x2b, 0xb5, 0xb5, 0xec, + 0x14, 0x63, 0x75, 0xf2, 0x7f, 0xcc, 0x09, 0xd1, 0x05, 0xc7, 0xbf, + 0xc5, 0x24, 0x71, 0x9a, 0xeb, 0xe0, 0x52, 0x83, 0x2b, 0x81, 0x62, + 0x5c, 0x7c, 0xbc, 0x39, 0x6f, 0xb3, 0x1f, 0x29, 0x33, 0x91, 0x97, + 0x92, 0xb1, 0x8a, 0xc2, 0x67, 0xc1, 0x1b, 0xa6, 0x3f, 0xf4, 0x07, + 0xff, 0x58, 0x93, 0x7e, 0x7c, 0x6b, 0xb6, 0x28, 0xe5, 0xe5, 0x76, + 0xab, 0xe5, 0xd4, 0xc3, 0x8b, 0xb9, 0x78, 0x7b, 0x3e, 0x88, 0xa4, + 0xbb, 0x30, 0xe8, 0xc9, 0xf4, 0x6a, 0x4b, 0xe8, 0x83, 0x9f, 0x8c, + 0xc8, 0xbf, 0x11, 0x12, 0xf4, 0xc1, 0xbc, 0x75, 0x54, 0x52, 0x94, + 0x91, 0x20, 0x6c, 0x06, 0xb5, 0xc4, 0xe7, 0x56, 0x8d, 0xe9, 0x60, + 0xf0, 0x69, 0x01, 0xa3, 0xaf, 0x0f, 0xe4, 0x3f, 0xd9, 0x16, 0x6d, + 0xb2, 0xce, 0xca, 0xf6, 0x09, 0x36, 0x85, 0x5e, 0xf4, 0x92, 0x60, + 0x42, 0x9a, 0x6f, 0x68, 0x54, 0x24, 0x9e, 0x74, 0x37, 0x8e, 0x22, + 0xb2, 0x09, 0x68, 0x88, 0xad, 0x7f, 0x1a, 0xaf, 0x4f, 0xb8, 0x61, + 0x80, 0x8a, 0x0e, 0x45, 0x28, 0x30, 0x13, 0x8f, 0xd1, 0x55, 0x92, + 0x44, 0x5f, 0x25, 0x7b, 0xad, 0x4e, 0xf4, 0xc3, 0x6e, 0x04, 0x00, + 0xe6, 0xf9, 0x66, 0x9f, 0xa8, 0xc5, 0xfb, 0x2f, 0x4a, 0x96, 0x08, + 0x3a, 0x3b, 0xed, 0xb8, 0xc8, 0xcf, 0x2b, 0x40, 0xa8, 0x90, 0xfb, + 0xf7, 0x75, 0x09, 0x18, 0x1f, 0x27, 0xfd, 0xc9, 0x6d, 0x0a, 0x41, + 0x43, 0x71, 0x39, 0xd7, 0x37, 0x72, 0xdb, 0x53, 0x3c, 0xd9, 0xa2, + 0x2a, 0x3e, 0x55, 0x32, 0xef, 0xb0, 0x0e, 0xdf, 0x9e, 0x05, 0xa2, + 0x8e, 0x3d, 0xf1, 0x6e, 0xef, 0xab, 0xd6, 0xfb, 0xeb, 0x25, 0xf2, + 0x8c, 0x09, 0x81, 0x5f, 0x9a, 0xcf, 0xbe, 0xe4, 0x78, 0x46, 0x06, + 0x05, 0x6e, 0x2a, 0x71, 0xc4, 0xb7, 0xd7, 0xbe, 0x4c, 0x67, 0xd1, + 0xc4, 0xbb, 0xf9, 0xfa, 0xfe, 0xca, 0xf4, 0x63, 0xfd, 0x04, 0xa3, + 0xe2, 0x8f, 0x17, 0x7f, 0x31, 0x1a, 0xe5, 0xf1, 0x29, 0x53, 0x72, + 0x9e, 0xe0, 0x5c, 0x8d, 0xcc, 0x62, 0xdf, 0xa4, 0x90, 0xef, 0x1b, + 0x97, 0xd9, 0xcb, 0xee, 0x90, 0xa7, 0x45, 0xf7, 0x5c, 0xb3, 0x78, + 0xfd, 0x6b, 0x4d, 0xf6, 0xd4, 0x13, 0x50, 0xb0, 0xee, 0x4f, 0xf1, + 0xbc, 0xb9, 0xc8, 0x55, 0xa6, 0x75, 0xe4, 0x41, 0x80, 0x82, 0xfa, + 0x10, 0xf0, 0xc0, 0x8b, 0x35, 0xd6, 0x54, 0x6b, 0x7f, 0xb9, 0xbc, + 0xd2, 0xa4, 0xd2, 0xef, 0x5a, 0x43, 0x8e, 0xc5, 0xab, 0xc2, 0x86, + 0x50, 0x96, 0xe1, 0xc1, 0x56, 0xfe, 0xa9, 0x55, 0x73, 0x70, 0x70, + 0xb0, 0x4a, 0xbb, 0x87, 0x44, 0x0a, 0xe3, 0xa9, 0x97, 0xa4, 0xa7, + 0x36, 0x40, 0xed, 0x7a, 0x53, 0x80, 0x38, 0xf6, 0x74, 0x23, 0x95, + 0xd8, 0x73, 0xcd, 0xde, 0x11, 0xcd, 0xca, 0x9c, 0xdf, 0x9e, 0xb2, + 0x3c, 0x4c, 0xaf, 0x6e, 0xda, 0xb6, 0x08, 0x01, 0xaf, 0xb4, 0xcd, + 0x86, 0xfe, 0x44, 0xdf, 0xa7, 0x94, 0x90, 0x45, 0x2b, 0x8f, 0x7e, + 0xa7, 0x93, 0x1a, 0xbb, 0x56, 0xa0, 0xf9, 0xc0, 0x0a, 0xf9, 0x1f, + 0x83, 0x73, 0x38, 0xfb, 0x84, 0x26, 0x5a, 0x95, 0xab, 0xc0, 0x9e, + 0x6a, 0xc6, 0x70, 0x3d, 0x47, 0xa1, 0x01, 0x27, 0x10, 0xa7, 0x65, + 0x68, 0x37, 0xf8, 0xde, 0xd9, 0x90, 0x19, 0xd8, 0xd8, 0x70, 0xd7, + 0x99, 0x6f, 0x82, 0x52, 0xc5, 0x28, 0x78, 0xa7, 0x4d, 0xea, 0xc7, + 0x5a, 0x08, 0x83, 0x0a, 0x87, 0x85, 0x48, 0xfe, 0x68, 0xeb, 0x1d, + 0x8a, 0x3b, 0xdb, 0x0e, 0x12, 0x9e, 0x61, 0x69, 0x37, 0x1c, 0xe1, + 0xe4, 0x59, 0x15, 0x3f, 0x61, 0x9e, 0x08, 0xd8, 0xc7, 0x73, 0xc1, + 0x4e, 0xa7, 0x7f, 0x2d, 0x68, 0x0f, 0xbc, 0x5f, 0x10, 0xc8, 0x5f, + 0x91, 0x4d, 0x4b, 0xfb, 0xd1, 0x3e, 0xb5, 0x52, 0x8e, 0x54, 0x28, + 0x6f, 0x7a, 0x2e, 0xee, 0x4f, 0x95, 0xb6, 0x2b, 0x32, 0x4b, 0xf0, + 0xa5, 0xbb, 0x25, 0x3b, 0x9b, 0x94, 0x35, 0xcd, 0x30, 0x5a, 0xc4, + 0x7e, 0x12, 0x92, 0x3d, 0x93, 0xa7, 0x07, 0x7a, 0x8b, 0x11, 0xe8, + 0x7b, 0x7b, 0x5d, 0x81, 0x59, 0x55, 0xe3, 0x1a, 0xaa, 0x86, 0xa9, + 0x6e, 0x01, 0x7b, 0x3d, 0xb6, 0x53, 0x3e, 0x94, 0xf9, 0x26, 0xf7, + 0xeb, 0x66, 0xea, 0x5e, 0xca, 0x35, 0x7d, 0x56, 0xa3, 0x38, 0x7d, + 0x53, 0xa2, 0x17, 0xbe, 0x7e, 0x05, 0xd9, 0xb7, 0x2e, 0x03, 0x9a, + 0x3b, 0x43, 0xad, 0x55, 0xbb, 0x86, 0xdd, 0xdc, 0xa6, 0x5a, 0x6c, + 0xfb, 0x13, 0x1a, 0x35, 0x28, 0x23, 0x69, 0xf6, 0x0e, 0xb3, 0x15, + 0x54, 0x57, 0x39, 0x83, 0x21, 0x93, 0x48, 0x25, 0x69, 0x21, 0xae, + 0xad, 0x1b, 0x56, 0xc0, 0x19, 0x28, 0xbe, 0x5a, 0xc9, 0x74, 0xbd, + 0x0a, 0xe3, 0x73, 0xdb, 0x5b, 0x01, 0xd8, 0x95, 0xc3, 0x73, 0x80, + 0xa8, 0x45, 0x5c, 0x56, 0xf1, 0xc3, 0x4e, 0x74, 0xaa, 0x0f, 0x57, + 0x1d, 0x97, 0x33, 0x93, 0xa2, 0xfb, 0x31, 0x71, 0x51, 0xb7, 0x6f, + 0xf0, 0x70, 0x4a, 0x77, 0x44, 0xf6, 0x27, 0xb5, 0x97, 0xfa, 0xac, + 0x49, 0x93, 0x45, 0x8a, 0xd4, 0x18, 0x59, 0x37, 0x14, 0x5a, 0x95, + 0x8a, 0x27, 0x8d, 0x54, 0xb7, 0x59, 0xde, 0xf7, 0x14, 0x5f, 0x70, + 0x00, 0xe5, 0x66, 0x66, 0xe9, 0xef, 0x62, 0xb7, 0xa7, 0x23, 0xc3, + 0xde, 0x53, 0x60, 0x91, 0xcb, 0x52, 0x95, 0xeb, 0x7f, 0x93, 0x00, + 0xa0, 0xf4, 0x12, 0x08, 0x7c, 0x00, 0xc7, 0x98, 0x2f, 0x0b, 0x78, + 0x60, 0xb4, 0xdb, 0x99, 0x2d, 0xb2, 0xdb, 0xe8, 0x2e, 0x69, 0x83, + 0xc8, 0x3f, 0xbc, 0x86, 0x2d, 0xa9, 0x69, 0x5a, 0xde, 0x5f, 0x2f, + 0x1b, 0xca, 0xc9, 0xe4, 0x8e, 0xc3, 0xac, 0x33, 0x7c, 0x82, 0x0e, + 0x9f, 0xf6, 0xeb, 0x05, 0xeb, 0x96, 0x11, 0xba, 0x00, 0x0f, 0x20, + 0x1d, 0x64, 0x57, 0x73, 0x1e, 0x08, 0xec, 0x97, 0x2b, 0x7c, 0x96, + 0x84, 0x80, 0x04, 0x4b, 0x87, 0x6c, 0xbc, 0xf2, 0x41, 0x34, 0x62, + 0x97, 0x03, 0xae, 0x4f, 0xe5, 0x4a, 0x5c, 0xef, 0xbd, 0x74, 0x91, + 0xf6, 0x06, 0xfc, 0x7a, 0x06, 0x99, 0x3d, 0x72, 0xfa, 0x5f, 0xd2, + 0x07, 0xfd, 0x6c, 0xfd, 0x3b, 0x01, 0x89, 0xbb, 0xc3, 0x7c, 0xcf, + 0x23, 0x3e, 0x05, 0x37, 0x77, 0x68, 0x3e, 0x6e, 0xf4, 0x1f, 0xb2, + 0xb3, 0xf3, 0x4b, 0xc2, 0xbb, 0x92, 0x1d, 0xc8, 0xf0, 0xc2, 0x9b, + 0xa9, 0x9f, 0x9b, 0xe0, 0x0c, 0x20, 0x53, 0x92, 0xc1, 0x1b, 0xb0, + 0x10, 0x2c, 0x47, 0xfc, 0x9a, 0x0f, 0x70, 0xe6, 0x77, 0xfc, 0xb5, + 0x34, 0xe3, 0x9d, 0x5f, 0xf5, 0x8a, 0x24, 0x3d, 0xdd, 0x70, 0x8b, + 0x21, 0xc7, 0x14, 0xbc, 0xb9, 0xc1, 0x0f, 0xf7, 0xea, 0xbc, 0xf4, + 0x7c, 0x9a, 0xe9, 0xa4, 0xfa, 0x8a, 0x5c, 0x20, 0x62, 0xb0, 0xa2, + 0xe2, 0x59, 0x1b, 0x51, 0x1b, 0x7c, 0x59, 0xf5, 0x02, 0x73, 0x95, + 0x65, 0x14, 0x5e, 0xee, 0x2f, 0xf7, 0x82, 0x67, 0x55, 0xd5, 0xeb, + 0x4d, 0xbf, 0xf5, 0xa9, 0x7b, 0x74, 0x88, 0x15, 0x7c, 0x09, 0x17, + 0x26, 0x51, 0xe2, 0xd0, 0xc0, 0xf0, 0x41, 0x01, 0x9d, 0x98, 0xce, + 0x91, 0x4e, 0xca, 0xfe, 0xa1, 0xaf, 0xde, 0xb1, 0x12, 0x39, 0xb2, + 0x15, 0xf8, 0x2e, 0xb3, 0xdf, 0xa4, 0x9f, 0x50, 0x19, 0x4d, 0xec, + 0x2b, 0xe9, 0x4f, 0x92, 0x8f, 0x34, 0xa4, 0x01, 0xb7, 0x65, 0x45, + 0xa8, 0x09, 0x7e, 0x83, 0xa9, 0x3e, 0x5b, 0x82, 0xbe, 0x8e, 0xe2, + 0xf2, 0x5b, 0x17, 0x1f, 0xca, 0x54, 0x71, 0x78, 0xa0, 0x9c, 0x90, + 0x62, 0x2f, 0xc8, 0x07, 0x3a, 0xb9, 0x2a, 0xb3, 0xbd, 0x5a, 0x0c, + 0x44, 0xf7, 0x92, 0x4a, 0xcd, 0x0b, 0xb9, 0xe4, 0x69, 0x7f, 0x53, + 0x0a, 0x62, 0xab, 0xd2, 0xbe, 0x2b, 0x1e, 0x37, 0x67, 0x12, 0xc4, + 0x97, 0xc0, 0x71, 0x0e, 0xed, 0x59, 0x17, 0x4a, 0xca, 0x56, 0x1a, + 0xb0, 0x25, 0x3c, 0x6d, 0xb5, 0x9d, 0xa1, 0x80, 0xc5, 0xc1, 0xc4, + 0x98, 0x2d, 0x5a, 0x3e, 0x95, 0x40, 0x6c, 0xcf, 0xfe, 0x41, 0x47, + 0x5d, 0xd3, 0xe5, 0xa5, 0x26, 0x06, 0x29, 0x80, 0xe0, 0xd4, 0x20, + 0xc3, 0x4b, 0x88, 0x19, 0x1e, 0x72, 0xea, 0xb7, 0x4d, 0x62, 0x14, + 0x85, 0x1a, 0x25, 0x9d, 0xee, 0x78, 0x56, 0xbf, 0x3a, 0x24, 0xbe, + 0xfc, 0x4f, 0x2f, 0x2c, 0x76, 0x80, 0xaa, 0x85, 0xb7, 0x0f, 0xb6, + 0x84, 0x44, 0x4b, 0xf8, 0xcf, 0x4e, 0x2d, 0x32, 0xde, 0x79, 0x43, + 0x43, 0x58, 0x24, 0x27, 0xea, 0x70, 0x91, 0x99, 0x9b, 0xff, 0xaa, + 0x73, 0x92, 0x74, 0xae, 0x66, 0x55, 0x8b, 0xe9, 0x4b, 0xf5, 0x3e, + 0x5c, 0x5f, 0x72, 0x6f, 0x17, 0xa5, 0xfb, 0xb1, 0x62, 0x2d, 0x05, + 0xac, 0xf2, 0xc7, 0x17, 0x35, 0xa2, 0x8f, 0xd4, 0x23, 0xd4, 0x99, + 0xe9, 0x44, 0x10, 0x4f, 0x77, 0x12, 0x76, 0x87, 0xc0, 0x96, 0x42, + 0x35, 0x3a, 0x15, 0x3b, 0x9b, 0x72, 0xe2, 0xa5, 0xe1, 0x0f, 0x31, + 0x06, 0x47, 0x59, 0xfe, 0x04, 0xc2, 0xff, 0xc2, 0xe1, 0x3d, 0xff, + 0x08, 0x4f, 0x4e, 0xad, 0xb7, 0x7b, 0x81, 0xd9, 0x81, 0x77, 0x22, + 0xfc, 0x34, 0xcb, 0x72, 0xb5, 0xba, 0x56, 0x0c, 0xb3, 0x2c, 0x4e, + 0x31, 0x43, 0xbf, 0x39, 0x3e, 0x97, 0x4c, 0x16, 0x8f, 0xb9, 0x6e, + 0x36, 0x46, 0xb2, 0x81, 0xce, 0xa6, 0x4e, 0xbe, 0xa0, 0x7d, 0x46, + 0xa7, 0x54, 0x92, 0x5d, 0x7d, 0x80, 0x49, 0x3c, 0x73, 0x5d, 0x37, + 0xea, 0x91, 0xb3, 0x62, 0xef, 0xfa, 0xcf, 0x6b, 0x60, 0x57, 0x58, + 0xf7, 0x51, 0x61, 0x04, 0xd1, 0x8f, 0xe1, 0x54, 0xd6, 0xe4, 0xfe, + 0x97, 0x34, 0xea, 0xed, 0xbb, 0xac, 0xa7, 0xf7, 0x8a, 0xc3, 0x85, + 0x24, 0x7d, 0x3b, 0x95, 0x8b, 0x5c, 0x15, 0x52, 0x38, 0xf5, 0x43, + 0x92, 0x1a, 0x11, 0x08, 0x5e, 0xa9, 0xca, 0xf7, 0xb2, 0x8b, 0xba, + 0x90, 0xf8, 0xb5, 0xd4, 0x68, 0xc0, 0x6c, 0x92, 0xfa, 0x26, 0x20, + 0x5f, 0xf6, 0xe8, 0x8c, 0x22, 0xed, 0xe9, 0x46, 0x63, 0x0e, 0xe3, + 0xcb, 0x61, 0xb5, 0xf8, 0xc6, 0x7d, 0x45, 0x4e, 0xfc, 0x8c, 0x0d, + 0x6f, 0x4d, 0xc6, 0x46, 0x2d, 0x6f, 0xb9, 0xf9, 0x61, 0x0d, 0xe4, + 0x9c, 0x11, 0xb7, 0xee, 0xf2, 0x10, 0x0f, 0xe1, 0x54, 0x4e, 0x45, + 0xf8, 0x83, 0xa7, 0x11, 0xc8, 0x9e, 0xb7, 0x1d, 0x08, 0xa4, 0xa8, + 0xe4, 0x40, 0x07, 0x51, 0x62, 0x3c, 0xfb, 0x4e, 0x1b, 0xd1, 0xd1, + 0x60, 0x46, 0xac, 0xb5, 0x98, 0xec, 0xbd, 0xe4, 0x15, 0x52, 0x4a, + 0x00, 0xba, 0xe2, 0x59, 0x13, 0x26, 0x88, 0x85, 0xca, 0x10, 0xaa, + 0x65, 0x2d, 0x18, 0x36, 0x8a, 0xbc, 0x2d, 0x69, 0x0f, 0xbd, 0xf4, + 0x82, 0x4a, 0x11, 0x0b, 0xc6, 0x70, 0xd6, 0x5b, 0x4c, 0xb0, 0x80, + 0x3b, 0x99, 0x12, 0x7e, 0x1d, 0xd8, 0xd3, 0x39, 0x8a, 0xed, 0xda, + 0x46, 0x1e, 0xcd, 0x0c, 0x23, 0xc5, 0xc4, 0xd0, 0xe1, 0x5f, 0x26, + 0x82, 0x1f, 0x28, 0xd1, 0xbd, 0xb2, 0xc4, 0xe1, 0x01, 0x3e, 0x09, + 0x47, 0xcd, 0xb3, 0x9f, 0x8b, 0xae, 0xaf, 0x8a, 0x41, 0xb1, 0x53, + 0x41, 0x92, 0xb8, 0x41, 0xff, 0x31, 0x87, 0xaa, 0xea, 0x78, 0xff, + 0x13, 0x5c, 0x06, 0x76, 0xe6, 0xbb, 0x7d, 0xfa, 0x23, 0x06, 0xbb, + 0xfd, 0x9c, 0x04, 0x1d, 0x89, 0xdf, 0x10, 0xe5, 0xe2, 0xa4, 0x50, + 0x5b, 0xcc, 0x24, 0x19, 0x21, 0xe5, 0x5a, 0x2b, 0x7b, 0x4c, 0x5c, + 0xfa, 0x83, 0x81, 0x85, 0x5e, 0x4e, 0x51, 0x66, 0xb5, 0xfa, 0x04, + 0x71, 0x54, 0x81, 0x6f, 0xcb, 0xee, 0x60, 0xff, 0x09, 0x4d, 0x6b, + 0xf6, 0x50, 0xce, 0x93, 0xd8, 0x58, 0xff, 0x2c, 0x15, 0x51, 0x40, + 0x99, 0x87, 0x93, 0x2a, 0x4a, 0x53, 0x04, 0xe2, 0xf5, 0xb5, 0xc6, + 0x8f, 0xd7, 0xab, 0x8c, 0x3c, 0x5d, 0xa4, 0x9c, 0xa5, 0x6f, 0xac, + 0x2e, 0x16, 0x8c, 0x69, 0xf9, 0x8c, 0x97, 0x90, 0x1a, 0x2a, 0x59, + 0xe8, 0x21, 0x34, 0x5f, 0xfc, 0x2d, 0x6c, 0x57, 0x6e, 0x13, 0x9c, + 0xa3, 0x6b, 0x12, 0xb2, 0x94, 0x27, 0xbc, 0xf9, 0x37, 0x42, 0xe1, + 0x84, 0x3a, 0xf7, 0xb3, 0x51, 0xdf, 0xfe, 0x3d, 0x01, 0x26, 0x72, + 0xad, 0xc3, 0x10, 0xc3, 0xb3, 0x45, 0xfa, 0x78, 0x45, 0xc6, 0xc7, + 0xf4, 0x24, 0x11, 0x78, 0xce, 0x56, 0x25, 0xe8, 0xa9, 0x9e, 0x57, + 0x01, 0xbe, 0x4a, 0x80, 0xc1, 0xc8, 0xc1, 0xde, 0x94, 0x13, 0xd8, + 0xf9, 0x48, 0xf4, 0x05, 0x32, 0x85, 0x59, 0xe6, 0xda, 0xb4, 0xec, + 0x7b, 0x2b, 0x03, 0x08, 0x0f, 0x7c, 0x17, 0xb6, 0x4b, 0x5a, 0x04, + 0x26, 0x3d, 0x71, 0xe2, 0x91, 0x18, 0xfe, 0x23, 0x77, 0x20, 0x47, + 0xbb, 0x1a, 0xd1, 0xb7, 0x5c, 0xbc, 0xab, 0xfb, 0xdf, 0x90, 0xa3, + 0xdd, 0x2a, 0xb1, 0x44, 0x2b, 0xbf, 0x29, 0xac, 0x55, 0x10, 0xa0, + 0x22, 0x96, 0x90, 0x79, 0x3c, 0x1b, 0x25, 0xa6, 0xed, 0x10, 0x7c, + 0x60, 0x05, 0x76, 0x72, 0xc3, 0xa5, 0xc0, 0x7f, 0x64, 0xfa, 0x3b, + 0x23, 0x88, 0x5b, 0x18, 0x04, 0x99, 0x72, 0x46, 0xdc, 0x73, 0x6f, + 0x08, 0x01, 0x3f, 0x91, 0xd4, 0x2a, 0x5b, 0x15, 0xa7, 0x4b, 0x25, + 0xce, 0xb5, 0xbb, 0x39, 0xab, 0x39, 0x87, 0x9e, 0xe1, 0x4d, 0x5a, + 0xdc, 0x8a, 0x89, 0xc3, 0x2f, 0x03, 0xd7, 0x2e, 0x4d, 0xc2, 0x56, + 0xc3, 0xf5, 0x7e, 0x1a, 0x7b, 0x48, 0xe5, 0x1c, 0xb0, 0xc3, 0xf8, + 0xcf, 0xbc, 0x95, 0xca, 0x1e, 0x74, 0x76, 0xba, 0x02, 0x85, 0xab, + 0x3a, 0xae, 0xaf, 0x7b, 0x59, 0x56, 0x6d, 0x31, 0x58, 0x23, 0x95, + 0x7f, 0x1e, 0x8c, 0x68, 0xde, 0xfb, 0x39, 0x5b, 0x00, 0x46, 0xea, + 0xf7, 0x1b, 0x4a, 0x5a, 0x9d, 0x29, 0x9b, 0x83, 0x51, 0x6f, 0x68, + 0x50, 0x10, 0x34, 0x44, 0x5b, 0x6a, 0x83, 0x03, 0x95, 0x3b, 0x03, + 0x22, 0x5e, 0x57, 0x17, 0x01, 0x19, 0x7f, 0xb8, 0x88, 0x79, 0xef, + 0x43, 0x5b, 0x2e, 0x76, 0x20, 0x6d, 0x41, 0x4a, 0xd0, 0x5f, 0x1b, + 0x77, 0x37, 0x39, 0x42, 0x93, 0x01, 0x1f, 0x9a, 0x90, 0x06, 0x8b, + 0x37, 0x07, 0xa9, 0x7a, 0x70, 0xed, 0x3b, 0xa3, 0x6e, 0x02, 0x66, + 0x71, 0xe3, 0x24, 0xd8, 0x30, 0x35, 0xe5, 0xd7, 0x17, 0x71, 0x12, + 0x30, 0x9c, 0x77, 0x38, 0x50, 0xed, 0xc3, 0x17, 0x5f, 0x31, 0x37, + 0x4b, 0x19, 0x64, 0xcf, 0x55, 0x6a, 0x3b, 0xea, 0xfa, 0xae, 0x46, + 0xbf, 0x5b, 0x8b, 0x03, 0x86, 0x9a, 0x6a, 0x77, 0xe9, 0xc8, 0x2d, + 0x19, 0x72, 0x1d, 0x96, 0xbf, 0xe4, 0x98, 0xb4, 0x37, 0xc2, 0x05, + 0xe1, 0x7c, 0x25, 0x81, 0x67, 0x1a, 0xd9, 0x49, 0x7c, 0x9f, 0x8c, + 0xf4, 0x4c, 0xa6, 0x71, 0x57, 0x73, 0x8a, 0xf5, 0x97, 0x90, 0x8b, + 0x91, 0x05, 0xbc, 0x5d, 0x9f, 0x68, 0x57, 0x03, 0x0e, 0xd7, 0xc7, + 0x7e, 0x80, 0x11, 0xab, 0xf5, 0x31, 0x72, 0x23, 0xa4, 0x09, 0x5d, + 0x2d, 0x9f, 0xcd, 0x7a, 0x81, 0x61, 0x70, 0x8a, 0xb4, 0xb8, 0xbe, + 0xa7, 0x78, 0xad, 0xfe, 0xa7, 0xc3, 0x2d, 0xff, 0xc5, 0x2c, 0x47, + 0xdd, 0xb0, 0x1c, 0x7e, 0x20, 0xfe, 0x2a, 0x37, 0x6f, 0x7c, 0x32, + 0xac, 0x87, 0xc3, 0x49, 0x80, 0x6b, 0x95, 0xe4, 0x23, 0x74, 0x81, + 0x04, 0x47, 0x10, 0x50, 0xdc, 0xad, 0xf1, 0xc5, 0xd4, 0x20, 0xfe, + 0xb5, 0x52, 0x28, 0x54, 0x38, 0x43, 0xff, 0xe9, 0x97, 0x24, 0x1b, + 0x83, 0xd8, 0x85, 0x6b, 0xd3, 0x35, 0x28, 0xae, 0x30, 0xe4, 0x64, + 0x15, 0xc6, 0x47, 0xad, 0xf1, 0x65, 0xb3, 0x71, 0x58, 0x94, 0xdf, + 0x50, 0x4e, 0x7e, 0xf9, 0xa2, 0xd6, 0x94, 0xf4, 0x20, 0x18, 0xe0, + 0x9b, 0x72, 0x54, 0xad, 0x9f, 0xff, 0xf7, 0xf6, 0x31, 0xad, 0x02, + 0xb6, 0xe9, 0x58, 0xbd, 0x46, 0x33, 0xab, 0xcb, 0x21, 0x7c, 0xe6, + 0xd1, 0xf8, 0x6a, 0x0e, 0xbb, 0xe6, 0x84, 0xe0, 0xa3, 0xf1, 0x27, + 0xc5, 0xbe, 0x31, 0x54, 0x5e, 0xce, 0x36, 0x4a, 0x85, 0xab, 0x09, + 0xb9, 0xb4, 0xf2, 0x15, 0xe9, 0x29, 0x5d, 0xaa, 0xcb, 0x2c, 0x54, + 0x03, 0x3f, 0x3d, 0x9f, 0xfa, 0xe4, 0x85, 0x13, 0x8c, 0xd4, 0xac, + 0x54, 0xcd, 0x91, 0x7f, 0xbc, 0x05, 0xef, 0xa7, 0xf9, 0x8a, 0x67, + 0x94, 0x5e, 0x05, 0xbd, 0x7a, 0xfd, 0x34, 0x62, 0x4e, 0x29, 0xb7, + 0xc0, 0x9a, 0x75, 0x23, 0x24, 0xee, 0x09, 0xee, 0x14, 0x5e, 0x85, + 0x1e, 0x26, 0x49, 0xc9, 0xc1, 0x1e, 0xd2, 0x48, 0x3e, 0x2a, 0x38, + 0x5d, 0x7b, 0x73, 0xd5, 0x62, 0xd4, 0xec, 0x0e, 0xbe, 0x1c, 0x05, + 0x02, 0x9a, 0x56, 0x71, 0x72, 0x4b, 0x09, 0x47, 0x93, 0xb8, 0xbb, + 0x53, 0x9f, 0x94, 0x4e, 0x7c, 0xb5, 0x00, 0xab, 0xc4, 0x6e, 0x35, + 0x79, 0x9f, 0x79, 0x2f, 0xc2, 0xd3, 0x33, 0x82, 0x96, 0x50, 0x39, + 0x72, 0xd3, 0xca, 0x17, 0x4f, 0x90, 0x75, 0x4d, 0x29, 0x3c, 0x10, + 0xba, 0x9f, 0x14, 0x70, 0xe0, 0x81, 0x1d, 0x2f, 0x61, 0x85, 0x58, + 0x80, 0x2f, 0x5f, 0x88, 0xcf, 0xa1, 0xc0, 0x24, 0x1d, 0xac, 0xab, + 0xce, 0xa4, 0xeb, 0x11, 0x27, 0xee, 0x73, 0x97, 0xe3, 0x4b, 0x6b, + 0x03, 0xf5, 0x01, 0xbb, 0x86, 0xc6, 0xdf, 0xbf, 0xde, 0x5e, 0xc6, + 0x9e, 0x7b, 0xb2, 0x5f, 0x29, 0x56, 0xef, 0x07, 0x15, 0x42, 0xba, + 0x41, 0x9f, 0x6b, 0x35, 0xe3, 0x07, 0x94, 0xd6, 0xbe, 0x36, 0xa6, + 0x14, 0xdb, 0xdd, 0x38, 0xcd, 0x8b, 0x2a, 0x6e, 0xd6, 0x92, 0x4c, + 0x5d, 0x5c, 0x57, 0xfb, 0xcc, 0xb7, 0x7d, 0x68, 0x7c, 0xa0, 0x81, + 0xbc, 0x66, 0x5f, 0x17, 0xbd, 0x8d, 0x00, 0xc0, 0x07, 0x0a, 0x4e, + 0x91, 0x6f, 0x1a, 0x23, 0xd1, 0x9f, 0x1c, 0x37, 0x70, 0x50, 0xf4, + 0xa7, 0xca, 0x1d, 0x5a, 0x6d, 0xb4, 0xc8, 0xe0, 0x55, 0x5e, 0x82, + 0xe2, 0x9d, 0x27, 0x52, 0xd0, 0x7c, 0x09, 0x7c, 0x7e, 0x8e, 0x98, + 0x58, 0x69, 0x27, 0xf1, 0x05, 0x66, 0xc5, 0xa3, 0xa5, 0xa8, 0x95, + 0xe0, 0xfd, 0xd1, 0x5f, 0x76, 0x76, 0x6c, 0x6b, 0x47, 0x67, 0x39, + 0xf9, 0x45, 0x9d, 0xfe, 0x02, 0xa8, 0xbe, 0x49, 0xcb, 0xd2, 0x51, + 0x3e, 0x6f, 0x27, 0x07, 0x53, 0x6e, 0x3c, 0xba, 0xfc, 0x05, 0x57, + 0x62, 0xb9, 0xdb, 0xe4, 0xb6, 0x3e, 0x32, 0x66, 0x77, 0xc2, 0xd6, + 0xb2, 0x82, 0x29, 0x51, 0x28, 0x5a, 0xf9, 0x01, 0xba, 0x31, 0xc8, + 0x4d, 0xdb, 0xce, 0xaf, 0x5f, 0xd0, 0xda, 0x8d, 0x19, 0x5c, 0x27, + 0x56, 0xf0, 0x8e, 0x5f, 0x1d, 0x28, 0x87, 0x06, 0x6b, 0xcb, 0xcf, + 0x5e, 0x21, 0xb1, 0xe6, 0x68, 0x24, 0x3d, 0xbc, 0x78, 0x11, 0xa0, + 0xa5, 0x8d, 0x39, 0x97, 0x0a, 0xe4, 0x70, 0x27, 0xb4, 0x5d, 0xa2, + 0x98, 0x30, 0x23, 0x86, 0xdc, 0x10, 0x93, 0xef, 0xfc, 0xaf, 0xfc, + 0xab, 0xf3, 0xc1, 0xae, 0x3f, 0x7c, 0x39, 0xc0, 0x55, 0xc7, 0xf6, + 0x64, 0xf8, 0x79, 0x61, 0x22, 0x48, 0xba, 0xd9, 0x20, 0x2e, 0xc1, + 0xf7, 0xe1, 0x55, 0xc7, 0x27, 0xab, 0x8d, 0xd2, 0xfe, 0xc9, 0xe2, + 0x0a, 0xd6, 0xa0, 0x2e, 0xa9, 0x2d, 0x3a, 0x07, 0x48, 0xf9, 0x76, + 0xd5, 0x2e, 0xa7, 0xb5, 0xca, 0xa7, 0xa0, 0xe2, 0x1d, 0xb1, 0x94, + 0xba, 0x17, 0x89, 0x8e, 0xfc, 0xa0, 0xc5, 0xea, 0x53, 0xdd, 0xfb, + 0x8c, 0xe2, 0xa6, 0xef, 0xad, 0xf8, 0x37, 0xbe, 0xb4, 0x12, 0xbd, + 0x5c, 0x42, 0x89, 0x3d, 0x76, 0xa3, 0x79, 0x0b, 0x36, 0xd3, 0x34, + 0x18, 0xee, 0xe0, 0x0e, 0x8a, 0x03, 0xad, 0xf7, 0x89, 0xfa, 0x09, + 0xfe, 0x79, 0x12, 0x5d, 0xad, 0x6f, 0x2b, 0xab, 0x22, 0x86, 0xf7, + 0xdc, 0x1c, 0x53, 0xbf, 0x90, 0xf2, 0x95, 0x7d, 0xeb, 0xdd, 0xb9, + 0xf9, 0x51, 0x0c, 0x50, 0xe8, 0x14, 0xe7, 0x7b, 0x13, 0x58, 0xc0, + 0x52, 0x52, 0x2d, 0x5b, 0x20, 0x65, 0x93, 0x52, 0x86, 0x70, 0x51, + 0x8e, 0x37, 0x44, 0x2c, 0x0c, 0xe3, 0x60, 0xb7, 0x36, 0xf6, 0xad, + 0xa1, 0x73, 0xc4, 0x5e, 0x43, 0xae, 0x29, 0x51, 0x11, 0x6f, 0x2b, + 0x7a, 0xd4, 0xaf, 0x21, 0x8a, 0xd0, 0x49, 0x1f, 0x03, 0x03, 0xbd, + 0x14, 0x6a, 0xdc, 0xef, 0x2d, 0xc5, 0xa7, 0x0d, 0x09, 0x4c, 0x7d, + 0x17, 0x00, 0x84, 0xe8, 0x48, 0x7d, 0x66, 0x6b, 0x6e, 0x47, 0x71, + 0xab, 0xd7, 0xd5, 0x2f, 0xa4, 0x17, 0x1f, 0xe9, 0xe0, 0x64, 0xbe, + 0xdb, 0x14, 0xd2, 0x43, 0x94, 0x00, 0xd9, 0x01, 0x11, 0x31, 0x89, + 0x0a, 0xdc, 0x43, 0xae, 0x93, 0xae, 0x46, 0x2f, 0xc1, 0x0e, 0x5b, + 0x4a, 0xe4, 0xab, 0x64, 0x9e, 0xa2, 0x21, 0xe6, 0x38, 0xd6, 0x6b, + 0x5c, 0xf0, 0xc1, 0x30, 0x3c, 0x66, 0x52, 0x64, 0x34, 0xcb, 0x6b, + 0x54, 0x51, 0xdc, 0xdc, 0x05, 0x9b, 0x8a, 0x32, 0xc4, 0x39, 0xcf, + 0xce, 0xbf, 0xa6, 0xad, 0xad, 0xcb, 0xf2, 0x02, 0x1b, 0x48, 0x4d, + 0x84, 0xb7, 0x3e, 0xd0, 0xd4, 0xd7, 0x54, 0x67, 0x43, 0xed, 0x95, + 0x1c, 0x89, 0x33, 0x8d, 0xc5, 0x4f, 0x9e, 0x8a, 0x70, 0xfd, 0xea, + 0x8f, 0x9b, 0x68, 0x9a, 0xe3, 0x13, 0xd7, 0xf0, 0x9f, 0xaf, 0x07, + 0x65, 0xdf, 0x25, 0x10, 0xba, 0x34, 0xf9, 0xd6, 0x6a, 0x01, 0x7a, + 0xfd, 0xf4, 0x85, 0x00, 0x29, 0x5f, 0xfa, 0x2b, 0x16, 0xa8, 0x79, + 0x2d, 0xea, 0xae, 0x6b, 0x63, 0x3e, 0xcd, 0x87, 0x16, 0x1a, 0xf0, + 0xa9, 0x44, 0xca, 0x12, 0xd4, 0xae, 0xb0, 0x13, 0x5b, 0xe2, 0x0c, + 0xa5, 0x06, 0x5e, 0xa2, 0x2d, 0xe3, 0x48, 0x18, 0xe1, 0x00, 0x42, + 0x10, 0x2b, 0xc8, 0x3c, 0x10, 0xf0, 0x6a, 0xa1, 0x4e, 0x62, 0x90, + 0x7f, 0xd8, 0xcf, 0x33, 0x03, 0x63, 0xd9, 0xce, 0x12, 0x0e, 0x63, + 0xa2, 0x20, 0xfe, 0xdd, 0xdd, 0x9f, 0xe0, 0x86, 0x9d, 0xe6, 0x8e, + 0x81, 0xf6, 0x9c, 0x97, 0x3b, 0x90, 0xcb, 0x97, 0xce, 0xb5, 0xeb, + 0xea, 0x19, 0x0d, 0xf0, 0xc2, 0x07, 0x97, 0xe3, 0x24, 0x5f, 0x82, + 0x13, 0xfc, 0x69, 0x40, 0x9e, 0x44, 0x4f, 0x3e, 0x21, 0xfa, 0x04, + 0x4d, 0xbb, 0x26, 0xf6, 0xed, 0x07, 0xcb, 0x1a, 0xfd, 0xcc, 0xa0, + 0x35, 0x62, 0x71, 0x6b, 0x62, 0xb6, 0x3e, 0x60, 0xe4, 0xf1, 0xfa, + 0x7b, 0xd7, 0xd8, 0xf4, 0x5d, 0x1b, 0x4e, 0x5b, 0xec, 0x08, 0x8f, + 0x1f, 0xe9, 0x9b, 0x23, 0x36, 0x12, 0xfb, 0xaf, 0xa8, 0x6f, 0x0f, + 0x51, 0xeb, 0xc7, 0xfa, 0x58, 0x62, 0x6e, 0x9b, 0x05, 0xb0, 0xc8, + 0xc8, 0x7f, 0x14, 0x84, 0xcb, 0x88, 0x7b, 0x3a, 0x0e, 0x20, 0xbd, + 0xca, 0x0b, 0x49, 0x15, 0x2e, 0xd0, 0xd6, 0xff, 0xba, 0x6c, 0x79, + 0xf1, 0x5a, 0x7b, 0x87, 0xad, 0xfc, 0x59, 0x30, 0x98, 0xf5, 0x76, + 0x4a, 0xd5, 0xc8, 0xf8, 0xb6, 0xbe, 0xf3, 0x97, 0x8d, 0x7e, 0x02, + 0x5b, 0x43, 0xd8, 0x4a, 0x37, 0xb5, 0xcb, 0x91, 0xb7, 0x04, 0xbb, + 0x51, 0xc0, 0x41, 0x17, 0x8d, 0x84, 0x5f, 0xb0, 0xda, 0x44, 0x89, + 0xdf, 0x07, 0x68, 0x58, 0x72, 0xfb, 0x79, 0x97, 0xd5, 0x8a, 0x82, + 0x64, 0x3d, 0x8f, 0x1b, 0x95, 0x7f, 0x4f, 0x51, 0x85, 0x09, 0x02, + 0x2d, 0x5b, 0xab, 0xe2, 0x15, 0xbb, 0x6a, 0x14, 0x8e, 0x8e, 0x58, + 0xc2, 0x7b, 0x58, 0x97, 0xce, 0xd0, 0x1a, 0x34, 0x45, 0x49, 0x2a, + 0x18, 0x30, 0xf0, 0xae, 0x47, 0x7a, 0x82, 0x2b, 0x37, 0x63, 0x37, + 0x65, 0x4a, 0x93, 0x21, 0x0f, 0x05, 0xe1, 0xe3, 0x5b, 0xf2, 0xe8, + 0xa4, 0x4f, 0x17, 0x4e, 0x4a, 0x36, 0x99, 0x7a, 0xb8, 0xf6, 0x07, + 0xb1, 0x62, 0xe2, 0x29, 0x3a, 0x1d, 0xf3, 0x16, 0x63, 0x96, 0x11, + 0xce, 0x00, 0x55, 0x64, 0x90, 0x90, 0x52, 0x4c, 0xbf, 0x6f, 0x6a, + 0xe1, 0x2e, 0x51, 0x1c, 0x09, 0xa0, 0x92, 0xd7, 0xc2, 0x89, 0x4d, + 0x9e, 0x8e, 0xac, 0x48, 0x93, 0x4f, 0x90, 0x7f, 0xbe, 0x4e, 0x79, + 0x66, 0x0b, 0x12, 0xf0, 0xb1, 0x80, 0x74, 0xca, 0x70, 0x94, 0x7b, + 0x86, 0xa6, 0xb0, 0xd9, 0xc7, 0x51, 0x82, 0x8e, 0xcc, 0xea, 0xee, + 0x97, 0xd7, 0xd0, 0xf2, 0x5d, 0x6a, 0x50, 0x05, 0xe7, 0xe6, 0x9b, + 0x97, 0x36, 0x17, 0x84, 0x64, 0x87, 0x4a, 0x3f, 0x6b, 0xca, 0x76, + 0x1f, 0x0e, 0x40, 0x6c, 0x8c, 0x83, 0xde, 0xef, 0xf4, 0x9a, 0x4e, + 0xd2, 0x92, 0x8c, 0x16, 0xb2, 0x6f, 0x29, 0xd6, 0xe2, 0xe0, 0x75, + 0xcd, 0x5f, 0x36, 0xbc, 0x47, 0x67, 0x77, 0xac, 0x53, 0x08, 0x88, + 0x22, 0x30, 0x98, 0xf3, 0xc7, 0xcc, 0xb7, 0x35, 0x96, 0x36, 0xf1, + 0xf0, 0x62, 0x65, 0x04, 0xd8, 0x81, 0x89, 0xdf, 0xb2, 0xcb, 0xce, + 0xbc, 0x38, 0xa3, 0xa4, 0x03, 0x7b, 0x12, 0x4e, 0x80, 0x7b, 0x23, + 0xc9, 0x75, 0xe1, 0xe4, 0xc9, 0xbf, 0xf3, 0x77, 0x88, 0xd1, 0x7a, + 0xf9, 0x59, 0x10, 0x34, 0xa0, 0xfd, 0x57, 0x8b, 0xa1, 0xbb, 0x90, + 0x21, 0xcc, 0x19, 0xf0, 0x0e, 0x63, 0x4b, 0xee, 0x1f, 0xcd, 0x6c, + 0x4b, 0xc7, 0x17, 0x0f, 0xaa, 0xfa, 0xfc, 0xa9, 0x73, 0xfd, 0xcb, + 0xbb, 0x17, 0xb4, 0x7f, 0x4a, 0x49, 0x3a, 0x4a, 0xe6, 0x7b, 0xa3, + 0xef, 0xa4, 0x51, 0x7b, 0x4c, 0xba, 0x45, 0x0f, 0xc5, 0x40, 0x66, + 0xbb, 0x0c, 0x2b, 0x38, 0xd3, 0x14, 0x18, 0x57, 0x37, 0x69, 0x96, + 0xaa, 0xcf, 0xd5, 0x83, 0xcf, 0xa7, 0x61, 0x16, 0xad, 0x86, 0x9d, + 0x5f, 0xc4, 0xc1, 0xf3, 0x35, 0xf0, 0xf9, 0xe4, 0xd4, 0x22, 0x66, + 0x62, 0x98, 0xe1, 0x80, 0xba, 0x9e, 0x24, 0x32, 0x32, 0xe2, 0x67, + 0xdd, 0xf2, 0x0d, 0x5d, 0x06, 0xfb, 0xfc, 0x77, 0x76, 0x78, 0xcf, + 0x81, 0xb5, 0x40, 0x6b, 0x7e, 0xfd, 0x45, 0xdb, 0xb3, 0x09, 0xeb, + 0x95, 0x87, 0x50, 0x21, 0x5e, 0x6f, 0xbd, 0x3b, 0x50, 0xfc, 0x7f, + 0xe4, 0x30, 0x66, 0xe1, 0x12, 0x9a, 0xe7, 0x92, 0x61, 0x88, 0x6b, + 0xb1, 0x38, 0x90, 0xc4, 0x5b, 0x85, 0x5c, 0x8a, 0xdc, 0x42, 0xba, + 0x03, 0x59, 0x44, 0xc5, 0x9f, 0x64, 0xc1, 0x31, 0x8a, 0xfc, 0xd1, + 0x10, 0xec, 0xc0, 0xf9, 0xd3, 0x73, 0xb4, 0x08, 0xd1, 0xed, 0xf6, + 0x81, 0x97, 0xa3, 0xfa, 0xf9, 0x3a, 0xc0, 0x0c, 0xeb, 0x30, 0xc9, + 0xf7, 0xec, 0x80, 0xc3, 0x7e, 0x78, 0x64, 0xd0, 0x0e, 0x8c, 0xaf, + 0x8a, 0xb8, 0x4d, 0xfc, 0x26, 0x6a, 0xe4, 0x5c, 0x15, 0x71, 0x36, + 0xa7, 0x25, 0x76, 0xd7, 0x00, 0xb7, 0xd6, 0x1d, 0xba, 0x02, 0x9a, + 0x0d, 0x39, 0x53, 0x4f, 0x78, 0x54, 0x58, 0x39, 0x7b, 0xc5, 0xe1, + 0x66, 0xcd, 0xb5, 0xc0, 0x32, 0x66, 0xd9, 0x43, 0xc9, 0xa3, 0xff, + 0x07, 0x2d, 0x84, 0x33, 0x0f, 0xf5, 0x66, 0xea, 0xa5, 0x9a, 0x68, + 0x88, 0xa0, 0x87, 0x0c, 0xa2, 0xc0, 0xf2, 0x28, 0xcb, 0xa8, 0x2c, + 0x8f, 0xb5, 0x7f, 0xe0, 0x36, 0x2c, 0x2c, 0x50, 0xbb, 0xe1, 0xc0, + 0xe5, 0xe3, 0xdf, 0x10, 0xa0, 0x7f, 0x1e, 0xe0, 0x81, 0x37, 0x51, + 0xa3, 0x2f, 0x3d, 0x41, 0x46, 0xaf, 0x66, 0xb6, 0x26, 0xdd, 0x18, + 0x44, 0xb1, 0x8e, 0x93, 0x7b, 0xe6, 0x5e, 0xf7, 0xb8, 0x06, 0xa8, + 0xfa, 0x86, 0x64, 0xfd, 0x29, 0xe7, 0x25, 0x2f, 0x1b, 0xed, 0x62, + 0x96, 0x9e, 0xe3, 0x97, 0x36, 0x08, 0xda, 0xd3, 0x44, 0x0b, 0x0f, + 0x44, 0x78, 0xe4, 0x89, 0xbe, 0xac, 0xe0, 0xfc, 0x31, 0xd1, 0x56, + 0x95, 0xf8, 0x8e, 0xd8, 0xc1, 0x52, 0xb6, 0x82, 0x53, 0x5e, 0x8c, + 0xae, 0xc5, 0x61, 0xca, 0xa7, 0x8e, 0xab, 0xbf, 0xc1, 0x4f, 0xee, + 0x7c, 0x9c, 0x97, 0x21, 0x7c, 0x05, 0x53, 0xdf, 0xb9, 0x7e, 0x81, + 0xde, 0xf9, 0x64, 0x8f, 0xb8, 0xa4, 0xbc, 0x63, 0xe4, 0x1a, 0x34, + 0x71, 0x77, 0x83, 0xaa, 0xdf, 0xc5, 0xbe, 0x6b, 0xd3, 0x7f, 0xc5, + 0x0b, 0xa7, 0x89, 0x57, 0xdc, 0x2e, 0xfc, 0xf2, 0x4a, 0xe1, 0x75, + 0xee, 0x7c, 0xde, 0x7e, 0x5b, 0x4e, 0x24, 0x82, 0x55, 0xff, 0x66, + 0xe1, 0x9a, 0x1f, 0x07, 0xf3, 0x16, 0xdb, 0xad, 0xc6, 0x1e, 0xa3, + 0xfe, 0x2f, 0x00, 0xc5, 0xbf, 0x4d, 0x55, 0x50, 0xe4, 0x0f, 0x71, + 0xee, 0x1d, 0x9f, 0x91, 0x5f, 0xce, 0xbb, 0x32, 0x84, 0xc6, 0xbd, + 0x31, 0xe1, 0xc8, 0x70, 0xc9, 0xa6, 0x70, 0x87, 0xf4, 0x2d, 0x9b, + 0x32, 0xdd, 0x61, 0x23, 0x0c, 0xc0, 0x31, 0x04, 0x47, 0xa2, 0xde, + 0xc0, 0x82, 0xa6, 0x21, 0x16, 0x59, 0xa7, 0x50, 0xbd, 0xb4, 0xc5, + 0x2c, 0x78, 0x94, 0xcc, 0xfe, 0x0a, 0x40, 0x91, 0x02, 0xfe, 0x71, + 0xc8, 0x90, 0x4f, 0xa3, 0xd4, 0xf8, 0x00, 0xe0, 0x13, 0x89, 0x37, + 0xb7, 0xd0, 0xde, 0xd8, 0x45, 0x08, 0xf2, 0xfb, 0xc1, 0x79, 0xae, + 0xb5, 0x55, 0x2d, 0x4e, 0x7d, 0x2e, 0xf4, 0x29, 0xa9, 0x1c, 0x29, + 0xc8, 0x4f, 0x3e, 0x7e, 0xc4, 0x52, 0xe1, 0xbe, 0xc8, 0xb0, 0x66, + 0xec, 0x59, 0x4c, 0x82, 0x9e, 0xf2, 0x6c, 0xf8, 0xa5, 0xdc, 0x03, + 0x41, 0x63, 0xd6, 0x0d, 0x31, 0xe3, 0x0d, 0x78, 0x07, 0xb0, 0xee, + 0xbb, 0x8f, 0x9d, 0x3b, 0x7b, 0x35, 0xf4, 0x56, 0x32, 0x85, 0x29, + 0xec, 0x1d, 0x4a, 0xfc, 0xe1, 0x8f, 0xc3, 0x95, 0x63, 0x00, 0x98, + 0xf5, 0x4e, 0x58, 0x25, 0xd7, 0xef, 0x0e, 0x36, 0x6b, 0x53, 0x85, + 0x4d, 0xf8, 0xc5, 0x80, 0xb8, 0xc8, 0xd2, 0x8c, 0xaf, 0x62, 0x0f, + 0xe1, 0x4d, 0x6d, 0x63, 0x93, 0x81, 0xe8, 0x81, 0xae, 0x0d, 0x90, + 0x26, 0x7d, 0x2a, 0x82, 0xf8, 0x26, 0xff, 0x31, 0x60, 0xcb, 0x42, + 0x1f, 0x17, 0x05, 0xf8, 0x0f, 0x3b, 0xd6, 0xd2, 0x8c, 0x5f, 0x99, + 0x8b, 0x3d, 0x4f, 0x42, 0x0a, 0x6f, 0xba, 0x23, 0x29, 0xa8, 0xe6, + 0xee, 0x05, 0xd0, 0x10, 0xdf, 0x8c, 0x0d, 0xd0, 0x57, 0x94, 0x80, + 0x4f, 0x3b, 0xbf, 0xda, 0xf0, 0xc2, 0x35, 0xcc, 0x1c, 0x9f, 0x5b, + 0xb8, 0x20, 0x2c, 0x8c, 0xeb, 0x89, 0xb7, 0x51, 0xd1, 0xef, 0x1a, + 0x11, 0xd4, 0x42, 0x0c, 0xb0, 0x70, 0xe6, 0x84, 0x80, 0x87, 0x78, + 0x98, 0xf6, 0x18, 0x33, 0x6e, 0x1a, 0x50, 0xa5, 0x2a, 0xa3, 0x7f, + 0x92, 0x95, 0x6d, 0xaf, 0x75, 0xd5, 0x19, 0xd6, 0xb9, 0xdf, 0x11, + 0x61, 0x76, 0x64, 0xc0, 0x0b, 0x62, 0x80, 0xc2, 0x7b, 0x3b, 0x64, + 0xb8, 0x69, 0x45, 0xe0, 0x5b, 0x50, 0xf6, 0x30, 0xbb, 0xcb, 0x6e, + 0xb3, 0x39, 0x54, 0xba, 0x0a, 0x1f, 0x11, 0xfc, 0x9d, 0xe4, 0xce, + 0x80, 0x72, 0xfd, 0x57, 0x6f, 0xfa, 0x03, 0x7c, 0x70, 0xf1, 0xbb, + 0xab, 0x2a, 0x94, 0x69, 0x0e, 0x4e, 0x2d, 0x94, 0xe9, 0xbf, 0x90, + 0x86, 0x2d, 0x11, 0x45, 0xaa, 0x5d, 0x00, 0x00, 0x73, 0xe6, 0x3f, + 0x5a, 0x80, 0x60, 0x53, 0x70, 0x6e, 0x77, 0x23, 0x8e, 0x69, 0x39, + 0x5c, 0x53, 0x31, 0xa0, 0x6e, 0x8a, 0x5b, 0xd8, 0x1b, 0x23, 0x1b, + 0xfa, 0xcd, 0xf6, 0x43, 0x39, 0x8a, 0x0c, 0xf9, 0x74, 0xf1, 0xf1, + 0x03, 0x42, 0x20, 0xea, 0xc3, 0x5f, 0xff, 0x40, 0x3c, 0x84, 0x25, + 0xec, 0xd8, 0x3d, 0x53, 0x03, 0x2c, 0xfb, 0xfa, 0x37, 0x9f, 0x70, + 0xea, 0xa8, 0xec, 0xc7, 0x7a, 0xab, 0x34, 0x0d, 0xf7, 0xcd, 0xdf, + 0x42, 0x81, 0xf2, 0xac, 0xd4, 0xb1, 0x61, 0x70, 0xf1, 0x29, 0xd5, + 0x42, 0xf7, 0x0a, 0xde, 0xa6, 0x9d, 0x40, 0x0d, 0x33, 0x45, 0x02, + 0xd8, 0x6e, 0xc4, 0xf2, 0x04, 0x52, 0xb9, 0xa5, 0x7a, 0x1a, 0x24, + 0x58, 0xb8, 0x60, 0xa3, 0xc6, 0x8b, 0xc9, 0x7f, 0xeb, 0x55, 0xf8, + 0x3b, 0x87, 0x55, 0x0b, 0xed, 0x1b, 0x88, 0x8b, 0x74, 0xde, 0xf0, + 0xaa, 0x3a, 0xc3, 0xbd, 0x92, 0x60, 0xba, 0x9f, 0x30, 0x7a, 0x37, + 0x65, 0x35, 0x27, 0xd7, 0xe8, 0xfd, 0x81, 0x6f, 0xa4, 0xb3, 0x6e, + 0xf1, 0xa2, 0x6d, 0x4c, 0xe5, 0x1c, 0x79, 0x66, 0xfa, 0x70, 0xbb, + 0x7d, 0xf7, 0xd1, 0x5d, 0x4a, 0x59, 0x51, 0xb1, 0x14, 0x6e, 0x58, + 0x88, 0x4d, 0xdc, 0x61, 0x9e, 0xdf, 0x38, 0x4d, 0xb4, 0x4c, 0xdb, + 0x1d, 0xc6, 0xdb, 0xaa, 0xbc, 0x08, 0x29, 0x23, 0xf3, 0x20, 0xb8, + 0xae, 0x9c, 0x8a, 0x22, 0x07, 0x12, 0x31, 0x5f, 0x63, 0xc3, 0x7b, + 0x8f, 0x7c, 0xf1, 0xf8, 0xf2, 0xc5, 0x48, 0xa2, 0xf2, 0x0d, 0xa1, + 0xc4, 0x47, 0xb9, 0x59, 0x7f, 0x95, 0x45, 0x13, 0xea, 0x0e, 0x33, + 0x49, 0x90, 0x8d, 0x80, 0xa9, 0xe0, 0x34, 0x6e, 0x46, 0xc8, 0x4c, + 0xcd, 0x1b, 0x5a, 0x11, 0xcc, 0x91, 0xb3, 0x31, 0xcb, 0x1e, 0xce, + 0x46, 0x1a, 0x70, 0x1b, 0x9a, 0xfc, 0x07, 0x03, 0xda, 0x4e, 0x99, + 0xff, 0xaf, 0x54, 0x78, 0x87, 0x73, 0xc1, 0x2d, 0xbf, 0x61, 0xc7, + 0xfa, 0x3a, 0xf7, 0xaa, 0xcb, 0x32, 0xcc, 0xee, 0xa4, 0xf0, 0x79, + 0x32, 0x9d, 0x53, 0x89, 0x0a, 0x30, 0xfd, 0x19, 0xb3, 0x40, 0x1b, + 0xa9, 0xec, 0x37, 0x31, 0x76, 0x94, 0x98, 0xf8, 0xcb, 0xfb, 0x98, + 0xd7, 0x96, 0x29, 0x15, 0xa8, 0x91, 0xc2, 0xbf, 0x7e, 0x50, 0xbe, + 0x55, 0x0b, 0x7c, 0xf7, 0xf1, 0x05, 0xcb, 0x22, 0xa6, 0xc7, 0x8e, + 0x59, 0xaa, 0xbf, 0x24, 0x4a, 0x1d, 0x52, 0xb4, 0x33, 0x5a, 0x84, + 0xe1, 0xad, 0xf7, 0xa6, 0xa4, 0xed, 0xa5, 0x2c, 0x33, 0x03, 0x0f, + 0x7c, 0x40, 0x7c, 0xad, 0x54, 0x93, 0x32, 0xa6, 0xc5, 0x31, 0xce, + 0x66, 0x58, 0x1b, 0xf4, 0x3c, 0x87, 0x01, 0xf3, 0xbd, 0x03, 0x0d, + 0x13, 0x8b, 0x40, 0x81, 0x0d, 0xe7, 0x0f, 0x6f, 0xa5, 0x11, 0x7a, + 0x08, 0x3e, 0x80, 0x4b, 0x50, 0x13, 0x75, 0x88, 0x96, 0xd6, 0xb6, + 0xfc, 0x5b, 0x3f, 0x98, 0x6a, 0x00, 0xed, 0x0d, 0x57, 0x85, 0x00, + 0x5a, 0x9a, 0xd5, 0xb9, 0x14, 0xfc, 0x17, 0xae, 0x4a, 0x25, 0x4a, + 0x2c, 0xb8, 0x17, 0x00, 0xa3, 0x09, 0xc4, 0x61, 0x19, 0xe9, 0x1e, + 0xa2, 0x5e, 0x48, 0xe5, 0x55, 0xb5, 0x28, 0x22, 0xea, 0xb1, 0x19, + 0x7f, 0x31, 0xa4, 0xab, 0x3b, 0xb8, 0x4d, 0xef, 0x7b, 0x3a, 0x5d, + 0x98, 0x36, 0x69, 0x8d, 0x57, 0xc3, 0xed, 0x5b, 0x1d, 0x92, 0x27, + 0xc3, 0x66, 0x9e, 0xf9, 0x5a, 0x09, 0xf9, 0xbb, 0x11, 0x19, 0x29, + 0xf8, 0xc0, 0x13, 0x9d, 0xf9, 0xeb, 0x61, 0x52, 0x51, 0x23, 0x26, + 0x4a, 0xa7, 0x6d, 0x0f, 0xc7, 0x8b, 0x67, 0xfd, 0x64, 0x77, 0x46, + 0x43, 0x0e, 0x7e, 0x45, 0x15, 0x69, 0x22, 0x4a, 0xb8, 0x9d, 0x27, + 0x68, 0x88, 0x57, 0xb8, 0xf7, 0x5c, 0xe0, 0xb7, 0x44, 0xc9, 0x08, + 0x90, 0x37, 0x0e, 0xf7, 0x17, 0x0a, 0xae, 0x54, 0xbf, 0x64, 0xf6, + 0x49, 0xe7, 0xcf, 0x17, 0x00, 0x5c, 0xdc, 0x38, 0x62, 0x11, 0xe2, + 0x4c, 0x3f, 0xde, 0xf7, 0xfc, 0x7b, 0x05, 0xf9, 0xf3, 0x9b, 0x54, + 0x79, 0x7a, 0x52, 0x32, 0xaa, 0x77, 0xf5, 0xbf, 0xde, 0xe4, 0x90, + 0xc0, 0xce, 0xd9, 0xa5, 0x43, 0x49, 0xb5, 0xfa, 0x1d, 0x9c, 0x46, + 0x9f, 0x0a, 0xf3, 0xb1, 0x73, 0xd7, 0xb6, 0xca, 0x89, 0x82, 0xd7, + 0x44, 0x42, 0x4e, 0x84, 0x58, 0xff, 0xee, 0xac, 0x66, 0x67, 0xe0, + 0x74, 0xec, 0x6c, 0xe5, 0xcc, 0x97, 0x21, 0x7e, 0x8d, 0x19, 0x2c, + 0xdb, 0x70, 0x42, 0xf1, 0xe3, 0xbf, 0x18, 0x53, 0x37, 0xb5, 0x7d, + 0x69, 0x3f, 0x75, 0xca, 0xcd, 0xd6, 0x99, 0xee, 0x12, 0x47, 0x40, + 0x6d, 0x36, 0x29, 0x82, 0x6d, 0x8d, 0x4a, 0xe5, 0x78, 0x13, 0xac, + 0x16, 0x96, 0x1d, 0x72, 0x20, 0xff, 0x7a, 0x03, 0xd6, 0xe7, 0xe8, + 0x58, 0xf8, 0x07, 0xf3, 0xbc, 0xb4, 0xbf, 0xfa, 0x4b, 0x0f, 0x7c, + 0x99, 0x98, 0x82, 0x77, 0xa1, 0xfc, 0x46, 0x7a, 0xd5, 0x19, 0x1f, + 0xe9, 0xcc, 0xfc, 0xc5, 0x68, 0x8a, 0x4a, 0x6d, 0x64, 0x30, 0x9a, + 0x09, 0x97, 0xd6, 0x70, 0x24, 0x08, 0x14, 0x21, 0x11, 0xdf, 0x38, + 0x09, 0xff, 0xa7, 0x73, 0x6b, 0x8f, 0x86, 0x3a, 0x8b, 0xe3, 0xbf, + 0xf1, 0xc8, 0xfb, 0xcd, 0xb2, 0x13, 0x66, 0x48, 0x94, 0x2d, 0x46, + 0x07, 0x85, 0x18, 0x89, 0x3c, 0x0a, 0xb3, 0xa4, 0xc4, 0x60, 0x16, + 0x35, 0x99, 0xf1, 0x8a, 0xf5, 0x7e, 0xcc, 0x10, 0x4a, 0x45, 0x5e, + 0x69, 0xc9, 0xa3, 0x58, 0x31, 0x64, 0x90, 0x77, 0x1a, 0x8f, 0x95, + 0x10, 0x9a, 0x94, 0x3c, 0x26, 0x8f, 0x21, 0xf2, 0x6a, 0xc2, 0xc4, + 0x34, 0x0c, 0x66, 0x7f, 0x76, 0xcf, 0xd9, 0x3d, 0x7b, 0xce, 0x9e, + 0xfd, 0x63, 0x7f, 0xe7, 0x77, 0xbe, 0xe7, 0x7b, 0xbf, 0xf7, 0x73, + 0x3f, 0xf7, 0xde, 0xef, 0x7d, 0x9c, 0xef, 0x39, 0xf7, 0xde, 0x78, + 0xb5, 0xd1, 0xb9, 0x5b, 0x74, 0xfe, 0x68, 0xcc, 0x88, 0x4b, 0xe1, + 0x37, 0xd0, 0xa1, 0x69, 0xfc, 0x66, 0x6f, 0x7c, 0xa6, 0x43, 0xdc, + 0xc2, 0x82, 0xb9, 0x38, 0x43, 0x7d, 0x45, 0xa1, 0x16, 0x3f, 0xf0, + 0x10, 0xe5, 0x6d, 0xa0, 0xe3, 0xb0, 0xb6, 0x52, 0x08, 0xc0, 0xf8, + 0xae, 0xcd, 0x35, 0x9f, 0xb8, 0xa4, 0xf3, 0x6b, 0xc3, 0x17, 0x3a, + 0x21, 0x9a, 0x13, 0x65, 0x87, 0x5b, 0x16, 0x17, 0xd7, 0x78, 0x06, + 0xa3, 0x4e, 0x68, 0x75, 0x8b, 0xec, 0x1b, 0x23, 0xf5, 0x81, 0x19, + 0x4d, 0xe5, 0xf7, 0x08, 0x91, 0x0f, 0x6b, 0xcd, 0x50, 0x26, 0x6d, + 0xd0, 0xd5, 0xbe, 0x0c, 0xba, 0x7f, 0xd1, 0xa7, 0xcd, 0x92, 0xba, + 0x79, 0x4e, 0x85, 0x53, 0x71, 0xf1, 0x05, 0x53, 0xf7, 0xc2, 0xd8, + 0x06, 0xa7, 0xd1, 0x03, 0x13, 0x3b, 0xe1, 0x6a, 0x56, 0xa7, 0x48, + 0x42, 0x69, 0xd4, 0xf3, 0xe2, 0x0f, 0xdd, 0x84, 0xd0, 0x7e, 0xfd, + 0x53, 0xee, 0x1a, 0x28, 0x72, 0x56, 0xc4, 0xda, 0xfa, 0x58, 0xc8, + 0x8a, 0x00, 0x18, 0x7f, 0x0a, 0xdb, 0x1b, 0x33, 0x0f, 0x4c, 0x48, + 0x00, 0x81, 0xdf, 0x78, 0xad, 0x02, 0xac, 0xc3, 0x4d, 0x5c, 0xc1, + 0xa3, 0x60, 0xb9, 0xd3, 0x60, 0x18, 0xd3, 0xc9, 0x13, 0x52, 0x49, + 0x54, 0x6c, 0x39, 0xbc, 0x5f, 0x86, 0xf1, 0x59, 0x6f, 0xe4, 0x44, + 0x2f, 0x26, 0xed, 0xb9, 0x4f, 0x86, 0x9e, 0xad, 0x43, 0x07, 0x1c, + 0xef, 0xf3, 0x6d, 0xc7, 0x25, 0xdb, 0x7c, 0xc8, 0xf2, 0x91, 0x67, + 0xe6, 0xd1, 0xe3, 0x56, 0x5d, 0x3c, 0x78, 0x9c, 0x84, 0x09, 0xea, + 0x60, 0x3a, 0xd1, 0x0c, 0xf2, 0xf4, 0x92, 0x55, 0x89, 0xb3, 0x23, + 0xe7, 0xb6, 0x10, 0x2c, 0x3d, 0xe1, 0x2e, 0xae, 0xe3, 0x71, 0x5e, + 0x7d, 0x72, 0xc9, 0xf0, 0xf0, 0x6e, 0x95, 0xb1, 0x31, 0x5b, 0xda, + 0xe4, 0x73, 0x7c, 0x4a, 0x7e, 0xe3, 0xda, 0xc1, 0x18, 0x3c, 0xce, + 0x95, 0xd2, 0x96, 0xa2, 0x2d, 0xd5, 0xa1, 0x75, 0xbe, 0x26, 0xf8, + 0x72, 0x34, 0xa5, 0x46, 0xaf, 0x9e, 0x31, 0x24, 0xb0, 0x76, 0x50, + 0x7a, 0x34, 0x91, 0x27, 0x54, 0xeb, 0x46, 0x55, 0x63, 0x98, 0x2d, + 0x23, 0xcc, 0xee, 0x5b, 0x51, 0xb7, 0xcc, 0x90, 0x0d, 0x3f, 0xae, + 0x4b, 0x31, 0x92, 0x4a, 0xa3, 0x8d, 0xe4, 0x5b, 0x4b, 0x26, 0xab, + 0x95, 0x5d, 0x10, 0x39, 0x5f, 0x11, 0xa1, 0x9a, 0x3e, 0x9b, 0x4b, + 0x5b, 0xac, 0xf2, 0x55, 0x81, 0x5a, 0x39, 0xf8, 0xcc, 0x92, 0xf4, + 0x83, 0xcc, 0x79, 0x28, 0x78, 0xdc, 0x54, 0x2b, 0x9e, 0xe8, 0x8c, + 0x38, 0x35, 0x76, 0x04, 0x55, 0x86, 0xb5, 0x4e, 0x70, 0xc0, 0xe3, + 0xda, 0xc8, 0x79, 0xf0, 0x69, 0x7b, 0x54, 0xb5, 0xb0, 0xf7, 0xfe, + 0x4b, 0x0e, 0x71, 0x43, 0x34, 0x5a, 0xbd, 0x76, 0xba, 0xa8, 0x79, + 0xef, 0x71, 0x4d, 0xef, 0x01, 0x61, 0x12, 0xe6, 0x67, 0x5b, 0xc6, + 0x14, 0xed, 0xab, 0x28, 0xf8, 0x04, 0x37, 0x92, 0xf2, 0x85, 0x29, + 0x8d, 0xf7, 0xe1, 0x6e, 0x53, 0xb9, 0xcf, 0x1f, 0x30, 0x8f, 0xdc, + 0xb3, 0x79, 0xf1, 0x71, 0x4d, 0xaf, 0x7e, 0x6a, 0x83, 0x1c, 0x96, + 0x6e, 0xa5, 0x76, 0x48, 0x56, 0xd8, 0xbb, 0xf6, 0x92, 0x03, 0x69, + 0x28, 0x97, 0x16, 0x97, 0x42, 0x44, 0xca, 0x89, 0xfa, 0x0b, 0x6e, + 0x93, 0x9c, 0x31, 0xa7, 0xe9, 0xf6, 0x25, 0x70, 0x69, 0x57, 0x24, + 0xdb, 0xeb, 0x30, 0xac, 0x75, 0x61, 0xab, 0x63, 0xe5, 0xec, 0x56, + 0xa0, 0x3e, 0x10, 0xeb, 0x11, 0x20, 0xfa, 0x87, 0xd4, 0x8e, 0xf4, + 0xdf, 0xa1, 0x8f, 0x44, 0x45, 0x26, 0x9b, 0xcf, 0x2c, 0xef, 0x23, + 0xa8, 0x65, 0x8c, 0x4b, 0xb2, 0x0f, 0xc4, 0x4d, 0x8a, 0xc5, 0x21, + 0xa5, 0x6c, 0xd2, 0x11, 0x9e, 0xcb, 0x02, 0x04, 0xb8, 0xc5, 0x5b, + 0x17, 0x24, 0xdb, 0xb1, 0x63, 0x21, 0xa9, 0x5d, 0xfb, 0xbe, 0x49, + 0x95, 0xfc, 0xf6, 0x5a, 0x78, 0xf4, 0x1e, 0xa7, 0x03, 0x5b, 0x9f, + 0x67, 0xd7, 0x1b, 0xd1, 0xa4, 0xb3, 0x6a, 0x19, 0xb3, 0x9f, 0x87, + 0xa0, 0xda, 0x91, 0xd4, 0x0e, 0x1b, 0x23, 0xce, 0x05, 0xd4, 0x8f, + 0x48, 0xc4, 0x0e, 0xba, 0x23, 0xd9, 0x31, 0xc9, 0x29, 0x71, 0x1e, + 0x77, 0xab, 0xa6, 0x98, 0x7d, 0xac, 0x33, 0xed, 0x30, 0x31, 0xb5, + 0xe3, 0x7c, 0xbb, 0xce, 0x81, 0xbc, 0xd9, 0x6d, 0x63, 0x93, 0x82, + 0xec, 0xcc, 0xb8, 0x05, 0x10, 0x2d, 0x9c, 0xa8, 0xcf, 0xb7, 0x0b, + 0x06, 0x17, 0x2a, 0xab, 0x1a, 0xd2, 0x42, 0x3c, 0x32, 0xf5, 0xd0, + 0xe4, 0x2b, 0xd3, 0x3d, 0x73, 0x36, 0x89, 0xf1, 0x7e, 0x55, 0x24, + 0x37, 0x17, 0x7d, 0x6b, 0x73, 0xbe, 0x4d, 0xdd, 0x9a, 0xf3, 0xa5, + 0xa8, 0x90, 0xe1, 0x21, 0x32, 0x3a, 0xc9, 0x3b, 0xb8, 0xaa, 0x3f, + 0x59, 0xa8, 0x67, 0x7e, 0x64, 0xd8, 0xfd, 0xf5, 0xa8, 0x79, 0x2e, + 0x63, 0x2e, 0x79, 0xdf, 0xfb, 0x93, 0x78, 0xdc, 0xf1, 0xa7, 0x62, + 0xd1, 0xbd, 0x9f, 0x16, 0x0d, 0x1c, 0xc5, 0xe4, 0xc7, 0x2f, 0x7e, + 0x68, 0x10, 0xfe, 0xe1, 0xaa, 0xed, 0x97, 0x62, 0x9d, 0xe6, 0x3b, + 0x8e, 0x88, 0xe1, 0xa1, 0x06, 0x61, 0x99, 0xab, 0x03, 0x5c, 0x94, + 0x57, 0x90, 0xb4, 0x46, 0xa1, 0x87, 0x63, 0x0b, 0xe2, 0xbe, 0xe5, + 0xdd, 0xdb, 0xa1, 0x3f, 0xf9, 0x67, 0xb8, 0xf8, 0xf8, 0x31, 0xac, + 0xf4, 0x2d, 0x4f, 0x1c, 0xa1, 0xe5, 0xf4, 0xcb, 0x26, 0xa5, 0x7b, + 0x38, 0x7e, 0xd0, 0x11, 0x2d, 0x82, 0x0c, 0xba, 0xeb, 0xa8, 0x97, + 0xe9, 0x32, 0xf1, 0xb8, 0x6e, 0x9b, 0xcb, 0x33, 0x2a, 0x83, 0x05, + 0x3f, 0xd8, 0xa3, 0xca, 0xe7, 0x47, 0x86, 0xc4, 0xa1, 0x3f, 0xac, + 0xef, 0xe3, 0x6b, 0x44, 0xfc, 0xf8, 0x00, 0x3e, 0xd2, 0x53, 0x49, + 0x72, 0x43, 0x5c, 0xb0, 0x62, 0xf3, 0x4f, 0x05, 0x35, 0x3f, 0x3b, + 0xd7, 0x51, 0x4e, 0x26, 0xb9, 0x69, 0xde, 0x1b, 0x1f, 0x70, 0xba, + 0xd5, 0xa5, 0xfa, 0xd8, 0x87, 0x4c, 0xa2, 0x34, 0x35, 0x95, 0xa5, + 0xe5, 0xde, 0x2e, 0x70, 0x58, 0xa8, 0xf0, 0xf1, 0x78, 0xf6, 0x73, + 0xf2, 0xe4, 0xe4, 0x27, 0x2e, 0xd6, 0x33, 0x1e, 0xad, 0xd5, 0xf8, + 0x38, 0x14, 0xd3, 0x42, 0x6e, 0xc9, 0x56, 0xc2, 0x0c, 0x30, 0x76, + 0xc1, 0x0d, 0xa5, 0x80, 0x95, 0xaa, 0x74, 0xb4, 0xaf, 0xb5, 0x20, + 0x94, 0x9b, 0x9a, 0x7d, 0xd9, 0xc3, 0xde, 0xa8, 0xfa, 0x28, 0xdd, + 0x85, 0xb5, 0x51, 0xb7, 0xf3, 0x61, 0x0b, 0xdd, 0xd7, 0x4a, 0x66, + 0xb1, 0x18, 0xe4, 0x3d, 0xab, 0xce, 0xf3, 0x89, 0xfa, 0xcf, 0xcb, + 0x3e, 0xa5, 0x0e, 0x1f, 0xd0, 0x7b, 0x58, 0x8f, 0xf0, 0x16, 0xfc, + 0xd8, 0xc8, 0x3b, 0xe5, 0xe4, 0x95, 0xc0, 0xa3, 0x9c, 0x48, 0x9a, + 0x51, 0xa7, 0x91, 0x1b, 0x68, 0x66, 0xb1, 0x02, 0x0b, 0xf7, 0xcc, + 0xa9, 0xdd, 0x1f, 0xf7, 0x0a, 0x45, 0x05, 0x06, 0xbe, 0x71, 0x64, + 0x3d, 0x1f, 0x66, 0xbd, 0xce, 0xff, 0x33, 0x5b, 0x5d, 0xa0, 0xba, + 0x17, 0xe2, 0x1b, 0x04, 0x3c, 0x50, 0x6c, 0xff, 0x9f, 0xaa, 0x26, + 0xc3, 0x56, 0x1c, 0x93, 0xc9, 0x3b, 0x3a, 0x0b, 0xe8, 0x1b, 0x10, + 0xff, 0x50, 0x9d, 0x2f, 0x46, 0xfd, 0x23, 0xfd, 0xef, 0x6a, 0xd3, + 0x12, 0xeb, 0x8c, 0x01, 0xc0, 0xd8, 0x5e, 0x89, 0xa1, 0x59, 0x9b, + 0x7b, 0xca, 0x96, 0x5f, 0x7e, 0x3a, 0xa7, 0x4a, 0x72, 0x96, 0xc0, + 0xfb, 0x10, 0xe8, 0x39, 0x53, 0x6a, 0x92, 0xeb, 0x83, 0x08, 0xbf, + 0xfe, 0x67, 0xb6, 0x88, 0x59, 0x57, 0xc7, 0xa8, 0x15, 0xb3, 0x34, + 0x54, 0xbb, 0x3d, 0xe5, 0xa7, 0x1f, 0xf1, 0xa3, 0x0b, 0x5c, 0x42, + 0xd4, 0x3c, 0x95, 0xd6, 0x23, 0x95, 0xb4, 0xa6, 0xd1, 0x52, 0x31, + 0x51, 0xff, 0x73, 0xd0, 0x93, 0xcc, 0xa3, 0x30, 0xf1, 0x46, 0xa9, + 0x78, 0x7e, 0x79, 0x1f, 0xb4, 0xba, 0x45, 0xa0, 0x7c, 0xa0, 0x5c, + 0x53, 0x60, 0xc8, 0x2b, 0xba, 0xfa, 0x2f, 0xba, 0xfe, 0x3c, 0x2a, + 0xf7, 0x6e, 0x13, 0x9b, 0x65, 0x35, 0xbf, 0x42, 0x44, 0x71, 0xfc, + 0x23, 0xe0, 0x95, 0x1b, 0xba, 0x7a, 0x57, 0x5c, 0xfb, 0xa1, 0xfd, + 0xd2, 0x9e, 0x8b, 0x8a, 0xb0, 0x79, 0xe7, 0xc6, 0x03, 0x95, 0x59, + 0xb2, 0x15, 0x39, 0x6e, 0x63, 0xe2, 0xe6, 0x2d, 0xaf, 0x94, 0x9a, + 0x23, 0x62, 0x31, 0x17, 0x56, 0x6c, 0x86, 0x75, 0x23, 0x0b, 0x23, + 0xe6, 0x73, 0x90, 0xf8, 0xef, 0x83, 0xbb, 0x90, 0x97, 0xa6, 0x3a, + 0xea, 0x6c, 0x14, 0x2c, 0x47, 0xc0, 0x1f, 0x51, 0xf9, 0x16, 0x5d, + 0xc3, 0xf1, 0x11, 0x4a, 0xaa, 0x4d, 0x48, 0x0e, 0x4f, 0xbc, 0xba, + 0xaa, 0xf1, 0x64, 0x86, 0xd2, 0x3c, 0x13, 0x80, 0x64, 0x4e, 0x8f, + 0x6a, 0xbd, 0x40, 0x9c, 0x1f, 0x09, 0xeb, 0xfb, 0x41, 0xd0, 0x22, + 0x88, 0xb8, 0xd4, 0x57, 0x21, 0x1f, 0xa8, 0x78, 0x91, 0xe2, 0x5e, + 0x61, 0x82, 0x2a, 0xe4, 0x46, 0x72, 0x45, 0x00, 0x53, 0x7c, 0xed, + 0x5d, 0x8d, 0x09, 0xb1, 0xbc, 0x84, 0xde, 0x53, 0x2a, 0x95, 0x03, + 0xb3, 0x2f, 0x1e, 0xd8, 0x49, 0xc2, 0x92, 0x98, 0xe1, 0x46, 0xca, + 0x1c, 0x3d, 0xda, 0xda, 0x81, 0x6e, 0x7d, 0x13, 0xce, 0xbe, 0x88, + 0xf0, 0x77, 0xd4, 0x52, 0x2c, 0x27, 0xa0, 0x21, 0xe6, 0x80, 0x3d, + 0xf3, 0xa2, 0xcd, 0x1c, 0xba, 0xd7, 0xa3, 0x7a, 0xfe, 0x2c, 0xab, + 0x65, 0xe6, 0x64, 0x6f, 0x80, 0xcb, 0x66, 0xc6, 0x94, 0xe6, 0xe4, + 0x75, 0x2f, 0x6a, 0x41, 0xbc, 0x9a, 0x97, 0x8e, 0x28, 0xb6, 0x4a, + 0xd5, 0x8e, 0x3d, 0xc7, 0xbc, 0xb5, 0x95, 0x67, 0x24, 0x97, 0x78, + 0x7d, 0xe6, 0xa0, 0x61, 0x5c, 0xa2, 0xf6, 0x72, 0x1b, 0x69, 0xc5, + 0xe0, 0x90, 0x3b, 0xcf, 0x4d, 0x19, 0xee, 0x60, 0xad, 0x5e, 0x9d, + 0x86, 0x9e, 0xae, 0x85, 0x5c, 0x29, 0x56, 0xdf, 0x8e, 0xea, 0xab, + 0x28, 0xab, 0x3d, 0xdd, 0x5b, 0xcb, 0x11, 0xea, 0xbd, 0x99, 0xae, + 0xb1, 0x79, 0xff, 0xbe, 0x4d, 0x33, 0xc8, 0xa5, 0x00, 0x8a, 0x86, + 0x7e, 0x34, 0xc8, 0x5c, 0x02, 0x8a, 0x13, 0xd7, 0x0e, 0x2f, 0x18, + 0x7e, 0x39, 0x37, 0x73, 0x43, 0xf5, 0x45, 0x5f, 0xc5, 0x62, 0xa7, + 0x8e, 0x03, 0x85, 0x00, 0x3d, 0x2c, 0x5a, 0x39, 0x60, 0x92, 0x5b, + 0x74, 0x6b, 0xe7, 0x14, 0x7f, 0x7e, 0xbb, 0x19, 0xd8, 0x7c, 0xb0, + 0x1e, 0x30, 0x69, 0x49, 0x1d, 0x06, 0x80, 0x02, 0x1f, 0xba, 0x9a, + 0xe2, 0x23, 0xf7, 0x9b, 0xd4, 0x2b, 0x59, 0xef, 0xdb, 0x1f, 0x11, + 0x76, 0xa7, 0x76, 0x0d, 0x7b, 0x5c, 0xef, 0x0e, 0xc6, 0x14, 0x64, + 0xc1, 0x91, 0x48, 0xae, 0xd2, 0xd7, 0x97, 0xd3, 0x21, 0x41, 0xed, + 0xb3, 0x15, 0xcb, 0x57, 0xbf, 0xb0, 0x35, 0x9d, 0x98, 0xee, 0x89, + 0xc5, 0xcd, 0x5e, 0x52, 0xea, 0xf3, 0x9b, 0x62, 0x9d, 0x93, 0xe3, + 0xd7, 0xd8, 0x96, 0x19, 0xe6, 0x9d, 0x7d, 0xbb, 0xe3, 0x35, 0xe7, + 0xbc, 0xe1, 0xd6, 0xb8, 0x9c, 0xed, 0x63, 0xa1, 0xbe, 0xf2, 0xa9, + 0x32, 0x12, 0x12, 0x09, 0x82, 0xef, 0x77, 0x50, 0x09, 0xa3, 0x38, + 0x51, 0xa9, 0x73, 0xf6, 0x0b, 0x7e, 0xf6, 0xb8, 0x25, 0xe8, 0x1d, + 0x8d, 0xd5, 0x73, 0xbc, 0xad, 0xf2, 0xa4, 0x6f, 0x61, 0x77, 0x94, + 0x95, 0x8d, 0x5d, 0x3b, 0x77, 0x1f, 0x16, 0xf1, 0x5d, 0x4f, 0x3b, + 0xe9, 0xbc, 0xf1, 0x5e, 0xa0, 0x1c, 0x2b, 0x40, 0x8c, 0xf0, 0x6a, + 0xf4, 0x26, 0x15, 0x18, 0x9f, 0x45, 0x87, 0xca, 0x48, 0xc0, 0xcf, + 0x7e, 0x6e, 0xd2, 0x7b, 0x5f, 0x8b, 0x4d, 0x9e, 0xe0, 0x2c, 0x97, + 0x63, 0xa9, 0xd2, 0xbc, 0xbf, 0x7c, 0xbf, 0x62, 0xd4, 0x20, 0xa2, + 0x62, 0xe6, 0xb4, 0xa4, 0x7d, 0xff, 0xe2, 0x4c, 0x71, 0x77, 0x69, + 0xaf, 0xc4, 0xb9, 0x1b, 0xbc, 0x76, 0xc7, 0xf8, 0xae, 0xfa, 0xb1, + 0x02, 0x0e, 0xd6, 0x62, 0xa1, 0xa3, 0x21, 0x5a, 0x43, 0x1c, 0xd7, + 0x94, 0xf0, 0xc9, 0xa6, 0xcd, 0xba, 0xc8, 0xb3, 0x08, 0xc1, 0xde, + 0x5b, 0x16, 0x12, 0x98, 0x6b, 0xca, 0xc4, 0x68, 0x0a, 0xda, 0x3d, + 0x47, 0x87, 0x7f, 0x2e, 0xea, 0xd8, 0x5a, 0x09, 0x0c, 0x00, 0x3e, + 0xd8, 0xc3, 0x8a, 0xdc, 0x11, 0xd7, 0xa4, 0x53, 0xb3, 0x16, 0x1b, + 0xbd, 0xd6, 0x01, 0x81, 0x55, 0x8a, 0x60, 0x54, 0x7e, 0xc8, 0x90, + 0x88, 0x7a, 0x68, 0xbc, 0xb2, 0x72, 0x80, 0x44, 0xf5, 0x32, 0xfa, + 0xa1, 0x31, 0xe9, 0x91, 0x11, 0xd4, 0xab, 0x7b, 0x61, 0x9a, 0x33, + 0xb2, 0x7d, 0x5d, 0x25, 0x40, 0xfe, 0x3b, 0x63, 0xe2, 0x32, 0x5a, + 0xd9, 0xd4, 0x70, 0xf0, 0xdd, 0xc5, 0x1c, 0x7a, 0x2a, 0xa3, 0x5c, + 0xf8, 0x54, 0x24, 0x84, 0x09, 0xbd, 0x25, 0xb9, 0x11, 0x9f, 0xfc, + 0xe5, 0x10, 0xf9, 0x0a, 0x38, 0x56, 0xdd, 0x1f, 0x78, 0x7d, 0xf5, + 0x92, 0x4e, 0x18, 0xd7, 0xae, 0xfd, 0xda, 0x12, 0x87, 0x39, 0x69, + 0x86, 0x28, 0xa0, 0xf3, 0xc9, 0xb1, 0xb1, 0x52, 0xd1, 0xda, 0xbf, + 0xd9, 0xbd, 0xb9, 0xbe, 0xc2, 0xfd, 0x1a, 0x2c, 0x2d, 0xb4, 0xd9, + 0xf8, 0x86, 0xb7, 0x67, 0x11, 0x02, 0x34, 0x2a, 0x11, 0x80, 0xf2, + 0xe7, 0xf7, 0xbf, 0x79, 0xe7, 0x91, 0x15, 0xc0, 0x19, 0xf6, 0x2e, + 0x16, 0x1c, 0x15, 0x10, 0x37, 0x78, 0x4d, 0x6a, 0xaf, 0x69, 0x32, + 0xce, 0x36, 0x0a, 0xc4, 0x70, 0x2b, 0x38, 0x96, 0x2f, 0xb7, 0xe8, + 0xd5, 0x1a, 0xf2, 0x92, 0xe0, 0x99, 0x98, 0x0d, 0xc8, 0xa4, 0x87, + 0xed, 0x27, 0xe9, 0xf9, 0x94, 0x89, 0x8c, 0xc4, 0x92, 0x8c, 0x7b, + 0x06, 0x5a, 0x2d, 0xfc, 0x59, 0x8e, 0x12, 0xdd, 0x79, 0xc7, 0x43, + 0xc7, 0xa1, 0xbe, 0x8d, 0xd9, 0x06, 0xa1, 0x2b, 0x55, 0x9d, 0xe4, + 0x27, 0x97, 0x53, 0x21, 0xfd, 0x59, 0xeb, 0x8b, 0x4f, 0xb9, 0x64, + 0x92, 0x69, 0x84, 0x2d, 0x95, 0xa7, 0xa7, 0x26, 0x56, 0xa3, 0xba, + 0x1a, 0x59, 0x06, 0x57, 0xb0, 0x7f, 0x23, 0xeb, 0x14, 0xe2, 0xb1, + 0x05, 0xe1, 0x5c, 0x17, 0x06, 0x1e, 0xba, 0x23, 0x06, 0x65, 0x9d, + 0xe6, 0x44, 0x24, 0x33, 0xe4, 0x22, 0x01, 0x03, 0x63, 0xa2, 0x2e, + 0x4e, 0x13, 0x95, 0x20, 0xc8, 0x18, 0x1a, 0x25, 0x18, 0xea, 0x09, + 0xbe, 0xf0, 0x84, 0x27, 0xd5, 0xc8, 0xaf, 0x32, 0x97, 0xf8, 0x1b, + 0xa0, 0xa7, 0x9d, 0x98, 0xa5, 0x63, 0x21, 0x5a, 0xf4, 0x28, 0x29, + 0xf1, 0xf8, 0x70, 0x63, 0xe2, 0x8a, 0xc5, 0xaa, 0x61, 0x52, 0xae, + 0x70, 0xd6, 0x8c, 0xa9, 0x95, 0xf2, 0x02, 0x64, 0x76, 0x03, 0x78, + 0x4c, 0x89, 0xa0, 0xf1, 0xa2, 0x08, 0x33, 0x49, 0x30, 0x84, 0x1e, + 0x10, 0xf7, 0x2b, 0x14, 0x20, 0x52, 0x09, 0x4f, 0x8a, 0x8d, 0xa0, + 0x1f, 0xdf, 0x20, 0x2d, 0x45, 0xd6, 0xee, 0x98, 0x46, 0x39, 0xf0, + 0x3e, 0xab, 0x51, 0x59, 0x88, 0x92, 0x02, 0xa9, 0x14, 0x67, 0xd0, + 0xf7, 0x76, 0x5b, 0xe5, 0xbb, 0x8a, 0xd6, 0x3a, 0xf0, 0xdc, 0x81, + 0xb4, 0x46, 0xc1, 0x48, 0x40, 0xc4, 0x59, 0x00, 0xae, 0xb4, 0x5b, + 0x27, 0x00, 0xcf, 0x9c, 0xa4, 0x78, 0x49, 0xa1, 0x68, 0x47, 0x7c, + 0xeb, 0xa6, 0x4e, 0x87, 0xe9, 0x9e, 0xa1, 0x60, 0x3c, 0xab, 0x6a, + 0xc5, 0x81, 0x57, 0x73, 0x02, 0x08, 0xc7, 0xa5, 0x00, 0xbd, 0xa4, + 0x55, 0x8a, 0xd5, 0xf7, 0x89, 0xeb, 0x10, 0x2b, 0xf7, 0x26, 0x71, + 0x0c, 0x64, 0x19, 0xf0, 0x67, 0x7d, 0x5e, 0x54, 0x86, 0x11, 0x6d, + 0x58, 0x39, 0x54, 0xfb, 0x2e, 0x33, 0xad, 0x79, 0x63, 0x21, 0x4b, + 0xdc, 0x66, 0x40, 0x24, 0xb2, 0x9d, 0x1c, 0x1c, 0x57, 0xfc, 0x51, + 0x35, 0xb7, 0xa8, 0xfc, 0x8a, 0xa8, 0xf8, 0xb5, 0xd7, 0x7b, 0x7e, + 0x5b, 0x07, 0xbe, 0xab, 0x13, 0x6f, 0xcf, 0xd1, 0x12, 0xc8, 0x0e, + 0xde, 0x09, 0x12, 0x5a, 0xed, 0x1f, 0x25, 0x6c, 0x15, 0x8c, 0xcb, + 0xc0, 0xcd, 0xcb, 0x5e, 0x32, 0x83, 0x32, 0xa9, 0x8b, 0x39, 0x95, + 0xe6, 0xa7, 0xb4, 0xd8, 0xc9, 0x03, 0x6d, 0x8b, 0x10, 0x4d, 0xfe, + 0xe0, 0x2b, 0x0a, 0x60, 0xbf, 0x9a, 0xe4, 0x31, 0x90, 0x28, 0xc8, + 0xdb, 0xf1, 0xbc, 0xe5, 0xb6, 0xfa, 0x93, 0xec, 0x97, 0x38, 0xd3, + 0xe6, 0x5e, 0x81, 0x9b, 0x99, 0x84, 0xaa, 0xda, 0x54, 0xb7, 0x79, + 0x9e, 0xee, 0xc2, 0xed, 0xf8, 0xda, 0x7c, 0x0f, 0xaf, 0xaf, 0xda, + 0x07, 0x05, 0x9f, 0x1a, 0x2e, 0xf0, 0x65, 0x43, 0x8f, 0x81, 0x3e, + 0xfc, 0xa3, 0x23, 0x81, 0xc1, 0x02, 0x98, 0xad, 0xc9, 0x81, 0x89, + 0xf8, 0xc5, 0x4b, 0x40, 0x77, 0x28, 0x56, 0x7f, 0xf3, 0xe4, 0xa2, + 0x7f, 0xd9, 0xe1, 0xd6, 0xe2, 0x8f, 0x45, 0x5b, 0x85, 0x60, 0x49, + 0x58, 0x0b, 0x7f, 0xf8, 0xe3, 0x38, 0x70, 0xad, 0x45, 0x6b, 0x9b, + 0x39, 0xaa, 0x9d, 0x8d, 0x17, 0xea, 0x7d, 0x65, 0x04, 0x8b, 0xdd, + 0xe8, 0xf4, 0x88, 0x68, 0xa5, 0xdb, 0xae, 0x60, 0xaf, 0x70, 0x7c, + 0x0b, 0x19, 0xd8, 0x1b, 0xfb, 0x5f, 0x09, 0x0d, 0x2a, 0x08, 0x03, + 0x2a, 0xd0, 0xd3, 0x10, 0xb8, 0xb9, 0xd7, 0x21, 0x20, 0xe1, 0xaa, + 0x28, 0x3f, 0x51, 0x32, 0xe5, 0x11, 0x20, 0x59, 0xa4, 0x23, 0x0b, + 0x98, 0xe9, 0x26, 0xf1, 0x98, 0xde, 0x7c, 0x89, 0x02, 0x04, 0xff, + 0x13, 0xf4, 0x72, 0xab, 0xed, 0x2f, 0x42, 0xb9, 0xff, 0xcf, 0xf5, + 0x37, 0x68, 0x5b, 0xec, 0xfc, 0x6b, 0xc3, 0xed, 0xa7, 0x77, 0xf6, + 0x95, 0x02, 0xe0, 0x67, 0x6d, 0x61, 0x67, 0x4e, 0x36, 0xc3, 0xc4, + 0xff, 0x0e, 0x50, 0x4b, 0x01, 0x02, 0x14, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0xac, 0x8a, 0x4d, 0xe1, 0xa5, 0x46, 0x24, + 0x30, 0x04, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x68, 0x61, 0x72, 0x2e, 0x70, 0x6e, + 0x67, 0x50, 0x4b, 0x01, 0x02, 0x3f, 0x03, 0x14, 0x03, 0x00, 0x00, + 0x08, 0x00, 0x3a, 0x92, 0x8d, 0x58, 0xda, 0xcf, 0x04, 0x24, 0xb1, + 0x56, 0x00, 0x00, 0x6c, 0x57, 0x00, 0x00, 0x0b, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80, 0xb4, 0x81, 0x56, + 0x04, 0x00, 0x00, 0x74, 0x65, 0x72, 0x72, 0x61, 0x69, 0x6e, 0x2e, + 0x70, 0x6e, 0x67, 0x0a, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x18, 0x00, 0x00, 0x78, 0x2f, 0x14, 0x7b, 0x8d, 0xda, + 0x01, 0x00, 0x78, 0x2f, 0x14, 0x7b, 0x8d, 0xda, 0x01, 0x00, 0x78, + 0x2f, 0x14, 0x7b, 0x8d, 0xda, 0x01, 0x50, 0x4b, 0x05, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x93, 0x00, 0x00, 0x00, + 0x30, 0x5b, 0x00, 0x00, 0x00, 0x00 +}; + +const int ccTextures_length = 23513; diff --git a/misc/ps1/iso.xml b/misc/ps1/iso.xml new file mode 100644 index 000000000..220f72635 --- /dev/null +++ b/misc/ps1/iso.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/misc/ps1/system.cnf b/misc/ps1/system.cnf new file mode 100644 index 000000000..e22172615 --- /dev/null +++ b/misc/ps1/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\template.exe;1 +TCB=4 +EVENT=10 +STACK=801FFFF0 diff --git a/misc/ps3/Makefile b/misc/ps3/Makefile index b37d83967..e28411b5a 100644 --- a/misc/ps3/Makefile +++ b/misc/ps3/Makefile @@ -35,7 +35,7 @@ ICON0 := ../misc/ps3/ICON0.png CFLAGS = -O2 -DPLAT_PS3 -Wall -mcpu=cell -fno-math-errno $(MACHDEP) $(INCLUDE) CXXFLAGS = $(CFLAGS) -LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map -fno-use-linker-plugin #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project @@ -141,4 +141,4 @@ $(OUTPUT).elf: $(OFILES) #--------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------- \ No newline at end of file +#--------------------------------------------------------------------------------- diff --git a/misc/saturn/Makefile b/misc/saturn/Makefile new file mode 100644 index 000000000..cd37ad8d1 --- /dev/null +++ b/misc/saturn/Makefile @@ -0,0 +1,28 @@ +ifeq ($(strip $(YAUL_INSTALL_ROOT)),) + $(error Undefined YAUL_INSTALL_ROOT (install root directory)) +endif + +SH_BUILD_DIR := build-saturn + +include $(YAUL_INSTALL_ROOT)/share/build.pre.mk + +# Each asset follows the format: ;. Duplicates are removed +BUILTIN_ASSETS= + +SH_PROGRAM := ClassiCube-saturn +SH_SRCS := $(wildcard src/*.c) + +SH_CFLAGS+= -Os -I. -DPLAT_SATURN +SH_LDFLAGS+= + +IP_VERSION:= V1.000 +IP_RELEASE_DATE:= 20160101 +IP_AREAS:= JTUBKAEL +IP_PERIPHERALS:= JAMKST +IP_TITLE:= VDP1 drawing +IP_MASTER_STACK_ADDR:= 0x06004000 +IP_SLAVE_STACK_ADDR:= 0x06001E00 +IP_1ST_READ_ADDR:= 0x06004000 +IP_1ST_READ_SIZE:= 0 + +include $(YAUL_INSTALL_ROOT)/share/build.post.iso-cue.mk diff --git a/misc/switch/Makefile b/misc/switch/Makefile new file mode 100644 index 000000000..d0d103f27 --- /dev/null +++ b/misc/switch/Makefile @@ -0,0 +1,226 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) +# +# NO_ICON: if set to anything, do not use icon. +# NO_NACP: if set to anything, no .nacp file is generated. +# APP_TITLE is the name of the app stored in the .nacp file (Optional) +# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) +# APP_VERSION is the version of the app stored in the .nacp file (Optional) +# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) +# ICON is the filename of the icon (.jpg), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .jpg +# - icon.jpg +# - /default_icon.jpg +# +# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .json +# - config.json +# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead +# of a homebrew executable (.nro). This is intended to be used for sysmodules. +# NACP building is skipped as well. +#--------------------------------------------------------------------------------- +TARGET := ClassiCube-switch +BUILD := build-switch +SOURCES := src misc/switch third_party/bearssl/src +DATA := data +INCLUDES := third_party/bearssl/inc +#ROMFS := romfs + +APP_TITLE := ClassiCube +APP_AUTHOR := UnknownShadow200 +ICON := misc/switch/icon.jpg + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lGLESv2 -lEGL -lglapi -ldrm_nouveau -lnx -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.jpg) + ifneq (,$(findstring $(TARGET).jpg,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).jpg + else + ifneq (,$(findstring icon.jpg,$(icons))) + export APP_ICON := $(TOPDIR)/icon.jpg + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_ICON)),) + export NROFLAGS += --icon=$(APP_ICON) +endif + +ifeq ($(strip $(NO_NACP)),) + export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp +endif + +ifneq ($(APP_TITLEID),) + export NACPFLAGS += --titleid=$(APP_TITLEID) +endif + +ifneq ($(ROMFS),) + export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/misc/switch/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... +ifeq ($(strip $(APP_JSON)),) + @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf +else + @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf +endif + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +ifeq ($(strip $(APP_JSON)),) + +all : $(OUTPUT).nro + +ifeq ($(strip $(NO_NACP)),) +$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp +else +$(OUTPUT).nro : $(OUTPUT).elf +endif + +else + +all : $(OUTPUT).nsp + +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm + +$(OUTPUT).nso : $(OUTPUT).elf + +endif + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/misc/switch/icon.jpg b/misc/switch/icon.jpg new file mode 100644 index 000000000..a8a2b5956 Binary files /dev/null and b/misc/switch/icon.jpg differ diff --git a/misc/wii/Makefile b/misc/wii/Makefile index b794a7b72..9af44eb58 100644 --- a/misc/wii/Makefile +++ b/misc/wii/Makefile @@ -33,7 +33,7 @@ LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lwiikeyboard -lwiiuse -lbte -lfat -logc -lm +LIBS := -lasnd -lwiikeyboard -lwiiuse -lbte -lfat -logc -lm #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -100,4 +100,4 @@ $(OUTPUT).elf: $(OFILES) #--------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------- \ No newline at end of file +#--------------------------------------------------------------------------------- diff --git a/misc/wiiu/Makefile b/misc/wiiu/Makefile new file mode 100644 index 000000000..f1031f62a --- /dev/null +++ b/misc/wiiu/Makefile @@ -0,0 +1,174 @@ +#------------------------------------------------------------------------------- +.SUFFIXES: +#------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) + +#------------------------------------------------------------------------------- +# APP_NAME sets the long name of the application +# APP_SHORTNAME sets the short name of the application +# APP_AUTHOR sets the author of the application +#------------------------------------------------------------------------------- +#APP_NAME := Application Name +#APP_SHORTNAME := App Name +#APP_AUTHOR := Built with devkitPPC & wut + +include $(DEVKITPRO)/wut/share/wut_rules + +#------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# CONTENT is the path to the bundled folder that will be mounted as /vol/content/ +# ICON is the game icon, leave blank to use default rule +# TV_SPLASH is the image displayed during bootup on the TV, leave blank to use default rule +# DRC_SPLASH is the image displayed during bootup on the DRC, leave blank to use default rule +#------------------------------------------------------------------------------- +TARGET := ClassiCube-wiiu +BUILD := build-wiiu +SOURCES := src third_party/bearssl/src +SHADERS := misc/wiiu +DATA := data +INCLUDES := third_party/bearssl/inc +CONTENT := +ICON := +TV_SPLASH := +DRC_SPLASH := + +#------------------------------------------------------------------------------- +# options for code generation +#------------------------------------------------------------------------------- +CFLAGS := -g -Wall -O2 -ffunction-sections -fno-math-errno \ + $(MACHDEP) + +CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ + +CXXFLAGS := $(CFLAGS) + +ASFLAGS := -g $(ARCH) +LDFLAGS = -g $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) + +LIBS := -lwut -lm + +#------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level +# containing include and lib +#------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(WUT_ROOT) + +#------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) \ + $(foreach dir,$(SHADERS),$(notdir $(wildcard $(dir)/*.gsh))) + +export LD := $(CXX) + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +ifneq (,$(strip $(CONTENT))) + export APP_CONTENT := $(TOPDIR)/$(CONTENT) +endif + +ifneq (,$(strip $(ICON))) + export APP_ICON := $(TOPDIR)/$(ICON) +else ifneq (,$(wildcard $(TOPDIR)/$(TARGET).png)) + export APP_ICON := $(TOPDIR)/$(TARGET).png +else ifneq (,$(wildcard $(TOPDIR)/icon.png)) + export APP_ICON := $(TOPDIR)/icon.png +endif + +ifneq (,$(strip $(TV_SPLASH))) + export APP_TV_SPLASH := $(TOPDIR)/$(TV_SPLASH) +else ifneq (,$(wildcard $(TOPDIR)/tv-splash.png)) + export APP_TV_SPLASH := $(TOPDIR)/tv-splash.png +else ifneq (,$(wildcard $(TOPDIR)/splash.png)) + export APP_TV_SPLASH := $(TOPDIR)/splash.png +endif + +ifneq (,$(strip $(DRC_SPLASH))) + export APP_DRC_SPLASH := $(TOPDIR)/$(DRC_SPLASH) +else ifneq (,$(wildcard $(TOPDIR)/drc-splash.png)) + export APP_DRC_SPLASH := $(TOPDIR)/drc-splash.png +else ifneq (,$(wildcard $(TOPDIR)/splash.png)) + export APP_DRC_SPLASH := $(TOPDIR)/splash.png +endif + +.PHONY: $(BUILD) clean all + +#------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/misc/wiiu/Makefile + +#------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).wuhb $(TARGET).rpx $(TARGET).elf + +#------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#------------------------------------------------------------------------------- +# main targets +#------------------------------------------------------------------------------- +all : $(OUTPUT).wuhb + +$(OUTPUT).wuhb : $(OUTPUT).rpx +$(OUTPUT).rpx : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +# you need a rule like this for each extension you use as binary data +#------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) +#------------------------------------------------------------------------------- +%.gsh.o %_gsh.h : ../misc/wiiu/%.gsh +#------------------------------------------------------------------------------- + echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#------------------------------------------------------------------------------- +endif +#------------------------------------------------------------------------------- diff --git a/misc/wiiu/coloured.gsh b/misc/wiiu/coloured.gsh new file mode 100644 index 000000000..22a1131e0 Binary files /dev/null and b/misc/wiiu/coloured.gsh differ diff --git a/misc/wiiu/coloured.psh b/misc/wiiu/coloured.psh new file mode 100644 index 000000000..5e702e991 --- /dev/null +++ b/misc/wiiu/coloured.psh @@ -0,0 +1,8 @@ +; $NUM_SPI_PS_INPUT_CNTL = 1 +; $SPI_PS_INPUT_CNTL[0].SEMANTIC = 0 +; $SPI_PS_INPUT_CNTL[0].DEFAULT_VAL = 1 +; $SQ_PGM_RESOURCES_PS.STACK_SIZE = 0 + +; -------- Disassembly -------------------- +00 EXP_DONE: PIX0, R0 +END_OF_PROGRAM diff --git a/misc/wiiu/coloured.psh.glsl b/misc/wiiu/coloured.psh.glsl new file mode 100644 index 000000000..19259de08 --- /dev/null +++ b/misc/wiiu/coloured.psh.glsl @@ -0,0 +1,6 @@ +varying vec4 out_col; + +void main() { + vec4 col = out_col; + gl_FragColor = col; +} \ No newline at end of file diff --git a/misc/wiiu/coloured.vsh b/misc/wiiu/coloured.vsh new file mode 100644 index 000000000..69440ed7e --- /dev/null +++ b/misc/wiiu/coloured.vsh @@ -0,0 +1,46 @@ +; $MODE = "UniformRegister" + +; $SPI_VS_OUT_CONFIG.VS_EXPORT_COUNT = 0 +; $NUM_SPI_VS_OUT_ID = 1 +; out_colour +; $SPI_VS_OUT_ID[0].SEMANTIC_0 = 0 + +; C0 +; $UNIFORM_VARS[0].name = "mvp" +; $UNIFORM_VARS[0].type = "mat4" +; $UNIFORM_VARS[0].count = 1 +; $UNIFORM_VARS[0].block = -1 +; $UNIFORM_VARS[0].offset = 0 + +; R1 +; $ATTRIB_VARS[0].name = "in_pos" +; $ATTRIB_VARS[0].type = "vec3" +; $ATTRIB_VARS[0].location = 0 +; R2 +; $ATTRIB_VARS[1].name = "in_col" +; $ATTRIB_VARS[1].type = "vec4" +; $ATTRIB_VARS[1].location = 1 + +; -------- Disassembly -------------------- +00 CALL_FS NO_BARRIER +01 ALU: ADDR(32) CNT(18) + 0 x: MUL ____, C3.w, 1.0f + y: MUL ____, C3.z, 1.0f + z: MUL ____, C3.y, 1.0f + w: MUL ____, C3.x, 1.0f + 1 x: MULADD R127.x, R1.z, C2.w, PV0.x + y: MULADD R127.y, R1.z, C2.z, PV0.y + z: MULADD R127.z, R1.z, C2.y, PV0.z + w: MULADD R127.w, R1.z, C2.x, PV0.w + 2 x: MULADD R127.x, R1.y, C1.w, PV1.x + y: MULADD R127.y, R1.y, C1.z, PV1.y + z: MULADD R127.z, R1.y, C1.y, PV1.z + w: MULADD R127.w, R1.y, C1.x, PV1.w + 3 x: MULADD R1.x, R1.x, C0.x, PV2.w + y: MULADD R1.y, R1.x, C0.y, PV2.z + z: MULADD R1.z, R1.x, C0.z, PV2.y + w: MULADD R1.w, R1.x, C0.w, PV2.x +02 EXP_DONE: POS0, R1 +03 EXP_DONE: PARAM0, R2 NO_BARRIER +END_OF_PROGRAM + diff --git a/misc/wiiu/coloured.vsh.glsl b/misc/wiiu/coloured.vsh.glsl new file mode 100644 index 000000000..e50bd9696 --- /dev/null +++ b/misc/wiiu/coloured.vsh.glsl @@ -0,0 +1,9 @@ +attribute vec3 in_pos; +attribute vec4 in_col; +varying vec4 out_col; +uniform mat4 mvp; + +void main() { + gl_Position = mvp * vec4(in_pos, 1.0); + out_col = in_col; +} diff --git a/misc/wiiu/shaders.txt b/misc/wiiu/shaders.txt new file mode 100644 index 000000000..ef11f59ad --- /dev/null +++ b/misc/wiiu/shaders.txt @@ -0,0 +1,14 @@ +Shader samples +https://github.com/decaf-emu/wiiu-tests/blob/master/content/shaders/pos_colour.vsh +https://github.com/GaryOderNichts/wiiu-shaders/blob/master/shaders.md +https://github.com/luRaichu/recsbr/blob/163fa441712f6b25e780e914617941c2385b330e/src/Backends/Rendering/WiiUShaders/shader%20sources/plain.vsh#L6 +https://github.com/GaryOderNichts/MultiDRCSpaceDemo/blob/30a6337a47dabafd6d601ce0555b326866f63247/shaders/textureShader.vsh#L6 +https://github.com/luRaichu/recsbr/blob/163fa441712f6b25e780e914617941c2385b330e/src/Backends/Rendering/WiiUShaders/shader%20sources/texture.vsh#L9 +https://github.com/rw-r-r-0644/gx2-texture/blob/83d7707e8d4b33ec7ba63d5c7dfe62c359b2b50a/shaders/texture_shader.vsh#L9 +https://github.com/yawut/ntrview-wiiu/blob/45b1c7f05cfd9917b8b171f3db08dc63293fc5c5/gfx/gx2_shaders/main.psh +https://github.com/Hydr8gon/NooDS/blob/b41cb3b9b889481c998e7e83db1ab3ef9d97b838/src/console/shaders/shader_wiiu.vsh +https://github.com/luRaichu/recsbr/blob/163fa441712f6b25e780e914617941c2385b330e/src/Backends/Rendering/WiiUShaders/shader%20sources/wtf%20is%20this.txt + +To generate shader assembly +1) Get AMD ShaderAnalyzer +2) Select Radeon HD 4670 (RV370) Assembly as the output format \ No newline at end of file diff --git a/misc/wiiu/textured.gsh b/misc/wiiu/textured.gsh new file mode 100644 index 000000000..cd3fb3bdd Binary files /dev/null and b/misc/wiiu/textured.gsh differ diff --git a/misc/wiiu/textured.psh b/misc/wiiu/textured.psh new file mode 100644 index 000000000..5bacbf261 --- /dev/null +++ b/misc/wiiu/textured.psh @@ -0,0 +1,21 @@ +; $MODE = "UniformRegister" +; $SAMPLER_VARS[0].name= "texImage" +; $SAMPLER_VARS[0].type= "sampler2D" +; $SAMPLER_VARS[0].location = 0 + +; $NUM_SPI_PS_INPUT_CNTL = 2 +; $SPI_PS_INPUT_CNTL[0].semantic = 0 +; $SPI_PS_INPUT_CNTL[0].default_val = 1 +; $SPI_PS_INPUT_CNTL[1].semantic = 1 +; $SPI_PS_INPUT_CNTL[1].default_val = 1 + +; -------- Disassembly -------------------- +00 TEX: ADDR(48) CNT(1) VALID_PIX + 0 SAMPLE R1, R1.xy0x, t0, s0 +01 ALU: ADDR(32) CNT(4) + 1 x: MUL R0.x, R0.x, R1.x + y: MUL R0.y, R0.y, R1.y + z: MUL R0.z, R0.z, R1.z + w: MUL R0.w, R0.w, R1.w +02 EXP_DONE: PIX0, R0 +END_OF_PROGRAM \ No newline at end of file diff --git a/misc/wiiu/textured.psh.glsl b/misc/wiiu/textured.psh.glsl new file mode 100644 index 000000000..6dc3f5880 --- /dev/null +++ b/misc/wiiu/textured.psh.glsl @@ -0,0 +1,9 @@ +varying vec4 out_col; +varying vec2 out_uv; +uniform sampler2D texImage; + +void main() { + vec4 col = texture(texImage, out_uv) * out_col; + gl_FragColor = col; +} + diff --git a/misc/wiiu/textured.vsh b/misc/wiiu/textured.vsh new file mode 100644 index 000000000..e6002b426 --- /dev/null +++ b/misc/wiiu/textured.vsh @@ -0,0 +1,55 @@ +; $MODE = "UniformRegister" + +; $SPI_VS_OUT_CONFIG.VS_EXPORT_COUNT = 1 +; $NUM_SPI_VS_OUT_ID = 1 +; out_colour +; $SPI_VS_OUT_ID[0].SEMANTIC_0 = 0 +; out_uv +; $SPI_VS_OUT_ID[0].SEMANTIC_1 = 1 + +; C0 +; $UNIFORM_VARS[0].name = "mvp" +; $UNIFORM_VARS[0].type = "mat4" +; $UNIFORM_VARS[0].count = 1 +; $UNIFORM_VARS[0].block = -1 +; $UNIFORM_VARS[0].offset = 0 + +; R1 +; $ATTRIB_VARS[0].name = "in_pos" +; $ATTRIB_VARS[0].type = "vec3" +; $ATTRIB_VARS[0].location = 0 +; R2 +; $ATTRIB_VARS[1].name = "in_col" +; $ATTRIB_VARS[1].type = "vec4" +; $ATTRIB_VARS[1].location = 1 +; R3 +; $ATTRIB_VARS[2].name = "in_uv" +; $ATTRIB_VARS[2].type = "vec2" +; $ATTRIB_VARS[2].location = 2 + +; -------- Disassembly -------------------- +00 CALL_FS NO_BARRIER +01 ALU: ADDR(32) CNT(20) + 0 x: MUL ____, C3.y, 1.0f + y: MUL ____, C3.x, 1.0f + z: MUL ____, C3.w, 1.0f + w: MUL ____, C3.z, 1.0f + t: MOV R0.x, R3.x + 1 x: MULADD R127.x, R1.z, C2.y, PV0.x + y: MULADD R127.y, R1.z, C2.x, PV0.y + z: MULADD R127.z, R1.z, C2.w, PV0.z + w: MULADD R127.w, R1.z, C2.z, PV0.w + t: MOV R0.y, R3.y + 2 x: MULADD R127.x, R1.y, C1.y, PV1.x + y: MULADD R127.y, R1.y, C1.x, PV1.y + z: MULADD R127.z, R1.y, C1.w, PV1.z + w: MULADD R127.w, R1.y, C1.z, PV1.w + 3 x: MULADD R1.x, R1.x, C0.x, PV2.y + y: MULADD R1.y, R1.x, C0.y, PV2.x + z: MULADD R1.z, R1.x, C0.z, PV2.w + w: MULADD R1.w, R1.x, C0.w, PV2.z +02 EXP_DONE: POS0, R1 +03 EXP: PARAM0, R2 NO_BARRIER +04 EXP_DONE: PARAM1, R0.xyzz NO_BARRIER +END_OF_PROGRAM + diff --git a/misc/wiiu/textured.vsh.glsl b/misc/wiiu/textured.vsh.glsl new file mode 100644 index 000000000..fe885892e --- /dev/null +++ b/misc/wiiu/textured.vsh.glsl @@ -0,0 +1,12 @@ +attribute vec3 in_pos; +attribute vec4 in_col; +attribute vec2 in_uv; +varying vec4 out_col; +varying vec2 out_uv; +uniform mat4 mvp; + +void main() { + gl_Position = mvp * vec4(in_pos, 1.0); + out_col = in_col; + out_uv = in_uv; +} diff --git a/misc/wiiu/textured_offset.vsh b/misc/wiiu/textured_offset.vsh new file mode 100644 index 000000000..fdca6f9e4 --- /dev/null +++ b/misc/wiiu/textured_offset.vsh @@ -0,0 +1,64 @@ +; $MODE = "UniformRegister" + +; $SPI_VS_OUT_CONFIG.VS_EXPORT_COUNT = 1 +; $NUM_SPI_VS_OUT_ID = 1 +; out_colour +; $SPI_VS_OUT_ID[0].SEMANTIC_0 = 0 +; out_uv +; $SPI_VS_OUT_ID[0].SEMANTIC_1 = 1 + +; C0 +; $UNIFORM_VARS[0].name = "mvp" +; $UNIFORM_VARS[0].type = "mat4" +; $UNIFORM_VARS[0].count = 1 +; $UNIFORM_VARS[0].block = -1 +; $UNIFORM_VARS[0].offset = 0 +; C4 (MIGHT BE WRONG AND SHOULD BE [3] instead??? ) +; $UNIFORM_VARS[1].name = "texOffset" +; $UNIFORM_VARS[1].type = "vec2" +; $UNIFORM_VARS[1].count = 1 +; $UNIFORM_VARS[1].block = -1 +; $UNIFORM_VARS[1].offset = 0 + +; R1 +; $ATTRIB_VARS[0].name = "in_pos" +; $ATTRIB_VARS[0].type = "vec3" +; $ATTRIB_VARS[0].location = 0 +; R2 +; $ATTRIB_VARS[1].name = "in_col" +; $ATTRIB_VARS[1].type = "vec4" +; $ATTRIB_VARS[1].location = 1 +; R3 +; $ATTRIB_VARS[2].name = "in_uv" +; $ATTRIB_VARS[2].type = "vec2" +; $ATTRIB_VARS[2].location = 2 + +; -------- Disassembly -------------------- +00 CALL_FS NO_BARRIER +01 ALU: ADDR(32) CNT(18) + 0 x: MUL ____, C3.y, 1.0f + y: MUL ____, C3.x, 1.0f + z: MUL ____, C3.w, 1.0f + w: MUL ____, C3.z, 1.0f + 1 x: MULADD R127.x, R2.z, C2.y, PV0.x + y: MULADD R127.y, R2.z, C2.x, PV0.y + z: MULADD R127.z, R2.z, C2.w, PV0.z + w: MULADD R127.w, R2.z, C2.z, PV0.w + 2 x: MULADD R127.x, R2.y, C1.y, PV1.x + y: MULADD R127.y, R2.y, C1.x, PV1.y + z: MULADD R127.z, R2.y, C1.w, PV1.z + w: MULADD R127.w, R2.y, C1.z, PV1.w + 3 x: MULADD R2.x, R2.x, C0.x, PV2.y + y: MULADD R2.y, R2.x, C0.y, PV2.x + z: MULADD R2.z, R2.x, C0.z, PV2.w + w: MULADD R2.w, R2.x, C0.w, PV2.z + 4 x: ADD R3.x, R3.x, C4.x + y: ADD R3.y, R3.y, C4.y +02 EXP_DONE: POS0, R2 +03 EXP: PARAM0, R1 NO_BARRIER +04 EXP_DONE: PARAM1, R3.xyzz NO_BARRIER +05 ALU: ADDR(50) CNT(1) + 5 x: NOP ____ +06 NOP NO_BARRIER +END_OF_PROGRAM + diff --git a/misc/wiiu/textured_offset.vsh.glsl b/misc/wiiu/textured_offset.vsh.glsl new file mode 100644 index 000000000..c1aa62130 --- /dev/null +++ b/misc/wiiu/textured_offset.vsh.glsl @@ -0,0 +1,13 @@ +attribute vec3 in_pos; +attribute vec4 in_col; +attribute vec2 in_uv; +varying vec4 out_col; +varying vec2 out_uv; +uniform mat4 mvp; +uniform vec2 texOffset; + +void main() { + gl_Position = mvp * vec4(in_pos, 1.0); + out_col = in_col; + out_uv = in_uv + texOffset; +} \ No newline at end of file diff --git a/misc/xbox/Makefile b/misc/xbox/Makefile index 9b0ec4b00..08761192f 100644 --- a/misc/xbox/Makefile +++ b/misc/xbox/Makefile @@ -8,6 +8,6 @@ SRCS = $(wildcard src/*.c) $(wildcard third_party/bearssl/src/*.c) SHADER_OBJS = misc/xbox/vs_coloured.inl misc/xbox/vs_textured.inl misc/xbox/ps_coloured.inl misc/xbox/ps_textured.inl NXDK_NET = y NXDK_CFLAGS = -Ithird_party/bearssl/inc -O1 -NXDK_LDFLAGS = -stack:131072 +NXDK_LDFLAGS = -stack:196608 include $(NXDK_DIR)/Makefile \ No newline at end of file diff --git a/misc/xbox/vs_coloured.vs.cg b/misc/xbox/vs_coloured.vs.cg index 1a070a3e2..b4e848f80 100644 --- a/misc/xbox/vs_coloured.vs.cg +++ b/misc/xbox/vs_coloured.vs.cg @@ -11,7 +11,8 @@ struct vOut { vOut main( vIn input, uniform float4x4 mvp, - uniform float4 half_viewport + uniform float4 vp_scale, + uniform float4 vp_offset ) { vOut result; @@ -21,9 +22,9 @@ vOut main( position = mul(position, mvp); position.xyz = position.xyz / position.w; - position.x = position.x * half_viewport.x + half_viewport.x; - position.y = -position.y * half_viewport.y + half_viewport.y; - position.z = position.z * half_viewport.z + half_viewport.z; + position.x = position.x * vp_scale.x + vp_offset.x; + position.y = position.y * vp_scale.y + vp_offset.y; + position.z = position.z * vp_scale.z + vp_offset.z; //position.w = 1.0 / half_viewport.w; result.pos = position; diff --git a/misc/xbox/vs_textured.vs.cg b/misc/xbox/vs_textured.vs.cg index c2a6f1c6f..bbad1efb1 100644 --- a/misc/xbox/vs_textured.vs.cg +++ b/misc/xbox/vs_textured.vs.cg @@ -13,7 +13,8 @@ struct vOut { vOut main( vIn input, uniform float4x4 mvp, - uniform float4 half_viewport + uniform float4 vp_scale, + uniform float4 vp_offset ) { vOut result; @@ -23,9 +24,9 @@ vOut main( position = mul(position, mvp); position.xyz = position.xyz / position.w; - position.x = position.x * half_viewport.x + half_viewport.x; - position.y = -position.y * half_viewport.y + half_viewport.y; - position.z = position.z * half_viewport.z + half_viewport.z; + position.x = position.x * vp_scale.x + vp_offset.x; + position.y = position.y * vp_scale.y + vp_offset.y; + position.z = position.z * vp_scale.z + vp_offset.z; //position.w = 1.0 / half_viewport.w; result.pos = position; diff --git a/misc/xbox360/ps_coloured.bin b/misc/xbox360/ps_coloured.bin new file mode 100644 index 000000000..5495b5dc6 Binary files /dev/null and b/misc/xbox360/ps_coloured.bin differ diff --git a/misc/xbox360/ps_coloured.h b/misc/xbox360/ps_coloured.h new file mode 100644 index 000000000..a48a75c39 --- /dev/null +++ b/misc/xbox360/ps_coloured.h @@ -0,0 +1,19 @@ +const char ps_coloured[160] = { + 0x10, 0x2a, 0x11, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x23, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x70, 0x73, 0x5f, 0x33, 0x5f, 0x30, 0x00, 0x32, 0x2e, + 0x30, 0x2e, 0x34, 0x33, 0x31, 0x34, 0x2e, 0x30, 0x00, 0xab, 0xab, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0xf0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0xc4, 0x00, + 0x22, 0x00, 0x00, 0x00, 0xc8, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const int ps_coloured_length = 160; diff --git a/misc/xbox360/ps_coloured.hlsl b/misc/xbox360/ps_coloured.hlsl new file mode 100644 index 000000000..06882f433 --- /dev/null +++ b/misc/xbox360/ps_coloured.hlsl @@ -0,0 +1,7 @@ +struct INPUT_VERTEX { + float4 col : COLOR0; +}; + +float4 main(INPUT_VERTEX input) : COLOR0 { + return input.col; +} \ No newline at end of file diff --git a/misc/xbox360/ps_textured.bin b/misc/xbox360/ps_textured.bin new file mode 100644 index 000000000..20e28f0fc Binary files /dev/null and b/misc/xbox360/ps_textured.bin differ diff --git a/misc/xbox360/ps_textured.h b/misc/xbox360/ps_textured.h new file mode 100644 index 000000000..17a0131c1 --- /dev/null +++ b/misc/xbox360/ps_textured.h @@ -0,0 +1,25 @@ +const char ps_textured[228] = { + 0x10, 0x2a, 0x11, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x00, 0xab, 0xab, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x73, + 0x5f, 0x33, 0x5f, 0x30, 0x00, 0x32, 0x2e, 0x30, 0x2e, 0x34, 0x33, + 0x31, 0x34, 0x2e, 0x30, 0x00, 0xab, 0xab, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3c, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x42, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x30, 0x50, 0x00, + 0x00, 0xf1, 0xa0, 0x00, 0x01, 0x10, 0x02, 0x00, 0x00, 0x12, 0x00, + 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x01, 0x1f, 0x1f, + 0xf6, 0x88, 0x00, 0x00, 0x40, 0x00, 0xc8, 0x0f, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const int ps_textured_length = 228; diff --git a/misc/xbox360/ps_textured.hlsl b/misc/xbox360/ps_textured.hlsl new file mode 100644 index 000000000..5f93ead15 --- /dev/null +++ b/misc/xbox360/ps_textured.hlsl @@ -0,0 +1,10 @@ +sampler s; + +struct INPUT_VERTEX { + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +float4 main(INPUT_VERTEX input) : COLOR0 { + return tex2D(s, input.uv) * input.col; +} \ No newline at end of file diff --git a/misc/xbox360/vs_coloured.bin b/misc/xbox360/vs_coloured.bin new file mode 100644 index 000000000..8373dfbc5 Binary files /dev/null and b/misc/xbox360/vs_coloured.bin differ diff --git a/misc/xbox360/vs_coloured.h b/misc/xbox360/vs_coloured.h new file mode 100644 index 000000000..3157423e6 --- /dev/null +++ b/misc/xbox360/vs_coloured.h @@ -0,0 +1,34 @@ +const char vs_coloured[324] = { + 0x10, 0x2a, 0x11, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x53, 0xff, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x76, 0x70, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x00, 0xab, + 0xab, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x73, 0x5f, 0x33, 0x5f, + 0x30, 0x00, 0x32, 0x2e, 0x30, 0x2e, 0x34, 0x33, 0x31, 0x34, 0x2e, + 0x30, 0x00, 0xab, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x84, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x90, + 0x00, 0x10, 0x00, 0x03, 0x00, 0x30, 0xa0, 0x04, 0x00, 0x00, 0xf0, + 0x50, 0x00, 0x00, 0x10, 0x09, 0x30, 0x05, 0x20, 0x03, 0x00, 0x00, + 0x12, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, + 0x00, 0x12, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x09, + 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf8, 0x20, + 0x00, 0x00, 0x00, 0x0e, 0x88, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, 0xc8, + 0x0f, 0x00, 0x01, 0x00, 0xb1, 0x77, 0x00, 0x81, 0x02, 0x01, 0x00, + 0xc8, 0x0f, 0x00, 0x01, 0x00, 0x6c, 0x94, 0xe3, 0xab, 0x02, 0x00, + 0x01, 0xc8, 0x0f, 0x00, 0x01, 0x00, 0xc6, 0x25, 0xe3, 0xab, 0x02, + 0x02, 0x01, 0xc8, 0x0f, 0x80, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x80, + 0x01, 0x03, 0x00, 0xc8, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const int vs_coloured_length = 324; diff --git a/misc/xbox360/vs_coloured.hlsl b/misc/xbox360/vs_coloured.hlsl new file mode 100644 index 000000000..78946889e --- /dev/null +++ b/misc/xbox360/vs_coloured.hlsl @@ -0,0 +1,20 @@ +struct INPUT_VERTEX +{ + float3 pos : POSITION; + float4 col : COLOR0; +}; + +struct OUTPUT_VERTEX +{ + float4 pos : POSITION; + float4 col : TEXCOORD0; +}; + +float4x4 mvpMatrix: register(c0); + +OUTPUT_VERTEX main(INPUT_VERTEX input) { + OUTPUT_VERTEX output; + output.pos = mul(mvpMatrix, float4(input.pos, 1.0f)); + output.col = input.col; + return output; +} \ No newline at end of file diff --git a/misc/xbox360/vs_textured.bin b/misc/xbox360/vs_textured.bin new file mode 100644 index 000000000..ba0ce7c6b Binary files /dev/null and b/misc/xbox360/vs_textured.bin differ diff --git a/misc/xbox360/vs_textured.h b/misc/xbox360/vs_textured.h new file mode 100644 index 000000000..ca65c47a4 --- /dev/null +++ b/misc/xbox360/vs_textured.h @@ -0,0 +1,37 @@ +const char vs_textured[360] = { + 0x10, 0x2a, 0x11, 0x01, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x53, 0xff, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x76, 0x70, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x00, 0xab, + 0xab, 0x00, 0x03, 0x00, 0x03, 0x00, 0x04, 0x00, 0x04, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x73, 0x5f, 0x33, 0x5f, + 0x30, 0x00, 0x32, 0x2e, 0x30, 0x2e, 0x34, 0x33, 0x31, 0x34, 0x2e, + 0x30, 0x00, 0xab, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x00, 0x11, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x42, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x90, + 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x30, 0x50, + 0x05, 0x00, 0x00, 0x30, 0x50, 0x00, 0x01, 0xf1, 0xa0, 0x00, 0x00, + 0x10, 0x0a, 0x00, 0x00, 0x10, 0x0b, 0x70, 0x15, 0x30, 0x03, 0x00, + 0x00, 0x12, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, + 0x00, 0x00, 0x12, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x0a, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xf8, + 0x30, 0x00, 0x00, 0x00, 0x0e, 0x88, 0x00, 0x00, 0x00, 0x00, 0x05, + 0xf8, 0x10, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x05, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc8, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x0f, 0x00, 0x02, 0x00, 0xb1, 0x77, 0x00, 0x81, 0x03, + 0x01, 0x00, 0xc8, 0x0f, 0x00, 0x02, 0x00, 0x6c, 0x94, 0xe3, 0xab, + 0x03, 0x00, 0x02, 0xc8, 0x0f, 0x00, 0x02, 0x00, 0xc6, 0x25, 0xe3, + 0xab, 0x03, 0x02, 0x02, 0xc8, 0x0f, 0x80, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x80, 0x02, 0x03, 0x00, 0xc8, 0x03, 0x80, 0x00, 0x00, 0xb0, + 0xb0, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xc8, 0x0f, 0x80, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xc2, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const int vs_textured_length = 360; diff --git a/misc/xbox360/vs_textured.hlsl b/misc/xbox360/vs_textured.hlsl new file mode 100644 index 000000000..d339fadb8 --- /dev/null +++ b/misc/xbox360/vs_textured.hlsl @@ -0,0 +1,23 @@ +struct INPUT_VERTEX +{ + float3 pos : POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +struct OUTPUT_VERTEX +{ + float4 pos : POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +float4x4 mvpMatrix: register(c0); + +OUTPUT_VERTEX main(INPUT_VERTEX input) { + OUTPUT_VERTEX output; + output.pos = mul(mvpMatrix, float4(input.pos, 1.0f)); + output.col = input.col; + output.uv = input.uv; + return output; +} \ No newline at end of file diff --git a/readme.md b/readme.md index 0568b154a..14424944d 100644 --- a/readme.md +++ b/readme.md @@ -21,9 +21,9 @@ ClassiCube is not trying to replicate modern Minecraft versions. It will never s You can **download ClassiCube** [from here](https://www.classicube.net/download/) and the very latest builds [from here](https://www.classicube.net/nightlies/). -![classic](https://github.com/ClassiCube/actions-testing-cc/assets/7892772/a233cb4c-296a-4d08-87fc-49874c230d4f) +![classic](https://github.com/ClassiCube/ClassiCube/assets/6509348/eedee53f-f53e-456f-b51c-92c62079eee0) -![enhanced](https://github.com/ClassiCube/actions-testing-cc/assets/7892772/61a064bd-cfaa-4a91-bedf-a16c3dd7e8a2) +![enhanced](https://github.com/ClassiCube/ClassiCube/assets/6509348/b2fe0e2b-5d76-41ab-909f-048d0ad15f37) # We need your help @@ -59,7 +59,7 @@ ClassiCube runs on: * macOS - 10.5 or later (but can be compiled to work with 10.3/10.4 though) * Linux - needs `libcurl` and `libopenal` * Android - 2.3 or later -* iOS - 10.3 or later +* iOS - 8.0 or later * Most web browsers (even runs on IE11) And also runs on: @@ -72,16 +72,21 @@ And also runs on: * BeOS - untested on actual hardware * IRIX - needs curl and openal packages * SerenityOS - needs SDL2 -* 3DS - unfinished, but usable (can [download from here](https://www.classicube.net/download/3ds)) +* Dreamcast - unfinished, but renders (can [download from here](https://www.classicube.net/download/dreamcast)) +* Switch - unfinished, but usable (can [download from here](https://www.classicube.net/download/switch)) +* Wii U - unfinished, major issues (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_wiiu.yml)), **untested on real hardware**) * Wii - unfinished, but usable (can [download from here](https://www.classicube.net/download/wii)) * GameCube - unfinished, but usable (can [download from here](https://www.classicube.net/download/gamecube)) -* Dreamcast - unfinished, but renders (can [download from here](https://www.classicube.net/download/dreamcast)) -* PSP - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/psp)) +* Nintendo 64 - unfinished, moderate rendering issues (can [download from here](https://www.classicube.net/download/n64)) +* 3DS - unfinished, but usable (can [download from here](https://www.classicube.net/download/3ds)) +* DS/DSi - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/nds)) * PS Vita - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/vita)) -* Xbox - unfinished, major rendering issues (can [download from here](https://www.classicube.net/download/xbox), **untested on real hardware**) -* PS3 - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/ps3), **usually outdated**) -* Nintendo 64 - unfinished, moderate rendering issues (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_n64.yml)) -* PS2 - unfinished, major rendering and **stability issues** (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_ps2.yml)) +* PSP - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/psp)) +* PS3 - unfinished, rendering issues (can [download from here](https://www.classicube.net/download/ps3)) +* PS2 - unfinished, major rendering and **stability issues** (can [download from here](https://www.classicube.net/download/ps2)) +* PS1 - unfinished, major rendering and **stability issues** +* Xbox 360 - completely unfinished (if you have a GitHub account, can [download from here](https://github.com/ClassiCube/ClassiCube/actions/workflows/build_xbox360.yml)), **untested on real hardware**) +* Xbox - unfinished, major rendering issues (can [download from here](https://www.classicube.net/download/xbox)) # Compiling @@ -115,13 +120,17 @@ I am assuming you used the installer from https://osdn.net/projects/mingw/ 4. Enter `gcc -fno-math-errno *.c -o ClassiCube.exe -mwindows -lwinmm -limagehlp` ##### Using TCC (Tiny C Compiler) -I am assuming you used `tcc-0.9.27-win64-bin.zip` from https://bellard.org/tcc/ -1. Extract the .zip file +Setting up TCC: +1. Download and extract `tcc-0.9.27-win64-bin.zip` from https://bellard.org/tcc/ 2. In TCC's `lib/kernel32.def`, add missing `RtlCaptureContext` at line 554 (In between `RtlAddFunctionTable` and `RtlDeleteFunctionTable`) -3. Copy `winapi` folder and `_mingw_dxhelper.h` from `winapi-full-for-0.9.27.zip` into TCC's `include` folder -4. Navigate to the directory with ClassiCube's source code -5. In `ExtMath.c`, change `fabsf` to `fabs` and `sqrtf` to `sqrtf` -6. Enter `tcc.exe -o ClassiCube.exe *.c -lwinmm -limagehlp -lgdi32 -luser32 -lcomdlg32 -lshell32` +3. Download `winapi-full-for-0.9.27.zip` from https://bellard.org/tcc/ +4. Copy `winapi` folder and `_mingw_dxhelper.h` from `winapi-full-for-0.9.27.zip` into TCC's `include` folder + +Compiling with TCC: +1. Navigate to the directory with ClassiCube's source code +2. In `ExtMath.c`, change `fabsf` to `fabs` and `sqrtf` to `sqrt` +3. Enter `tcc.exe -o ClassiCube.exe *.c -lwinmm -limagehlp -lgdi32 -luser32 -lcomdlg32 -lshell32`
+(Note: You may need to specify the full path to `tcc.exe` instead of just `tcc.exe`) ## Compiling - Linux @@ -146,14 +155,10 @@ Although the regular linux compiliation flags will work fine, to take full advan ## Compiling - macOS -##### Using gcc/clang (32 bit) - -```cc -fno-math-errno *.c -o ClassiCube -framework Carbon -framework AGL -framework OpenGL -framework IOKit``` - -##### Using gcc/clang (64 bit) - ```cc -fno-math-errno *.c interop_cocoa.m -o ClassiCube -framework Cocoa -framework OpenGL -framework IOKit -lobjc``` +Note: You may need to install Xcode before you can compile ClassiCube + ## Compiling - for Android NOTE: If you are distributing a modified version, **please change the package ID from `com.classicube.android.client` to something else** - otherwise Android users won't be able to have both ClassiCube and your modified version installed at the same time on their Android device @@ -180,7 +185,125 @@ Import `ios/CCIOS.xcodeproj` project into Xcode (TODO explain more detailed) `xcodebuild -sdk iphoneos -configuration Debug` (TODO explain more detailed) -## Compiling - other desktop OSes +## Compiling - webclient + +```emcc *.c -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=1Mb --js-library interop_web.js``` + +The generated javascript file has some issues. [See here for how to fix](doc/compile-fixes.md#webclient-patches) + +For more details on how to integrate into a website, see [here](doc/hosting-webclient.md) + +
+

Compiling - consoles

+ +All console ports needs assistance from someone experienced with homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) + +
+

Nintendo consoles (click to expand)

+ +#### Switch + +Run `make switch`. You'll need [libnx](https://github.com/switchbrew/libnx) and [mesa](https://github.com/devkitPro/mesa) + +**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `switch-dev` group and the `switch-mesa switch-glm` packages)** + +#### Wii U + +Run `make wiiu`. You'll need [wut](https://github.com/devkitPro/wut/) + +**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `wiiu-dev` group)** + +#### 3DS + +Run `make 3ds`. You'll need [libctru](https://github.com/devkitPro/libctru) + +**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `3ds-dev` group)** + +#### Wii + +Run `make wii`. You'll need [libogc](https://github.com/devkitPro/libogc) + +**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `wii-dev` group)** + +#### GameCube + +Run `make gamecube`. You'll need [libogc](https://github.com/devkitPro/libogc) + +**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `gamecube-dev` group)** + +#### Nintendo DS/DSi + +Run `make ds`. You'll need [BlocksDS](https://github.com/blocksds/sdk) + +#### Nintendo 64 + +Run `make n64`. You'll need the opengl branch of [libdragon](https://github.com/DragonMinded/libdragon/tree/opengl) + +
+ + +
+

Sony consoles (click to expand)

+ +#### PlayStation Vita + +Run `make vita`. You'll need [vitasdk](https://vitasdk.org/) + +#### PlayStation Portable + +Run `make psp`. You'll need [pspsdk](https://github.com/pspdev/pspsdk) + +**NOTE: It is recommended that you install the precompiled pspsdk version from [here](https://github.com/pspdev/pspdev/releases)** + +#### PlayStation 3 + +Run `make ps3`. You'll need [PSL1GHT](https://github.com/ps3dev/PSL1GHT) + +#### PlayStation 2 + +Run `make ps2`. You'll need [ps2sdk](https://github.com/ps2dev/ps2sdk) + +#### PlayStation 1 + +Run `make ps1`. You'll need [PSn00bSDK](https://github.com/Lameguy64/PSn00bSDK/) + +
+ + +
+

Microsoft consoles (click to expand)

+ +#### Xbox 360 + +Run `make 360`. You'll need [libxenon](https://github.com/Free60Project/libxenon) + +#### Xbox (original) + +Run `make xbox`. You'll need [nxdk](https://github.com/XboxDev/nxdk) + +
+ + +
+

SEGA consoles (click to expand)

+ +### SEGA consoles + +#### Dreamcast + +Run `make dreamcast`. You'll need [KallistiOS](https://github.com/KallistiOS/KallistiOS) + +#### Saturn + +Run `make saturn`. You'll need [libyaul](https://github.com/yaul-org/libyaul) + +
+ +
+ + +
+

Compiling - other platforms (click to expand)

#### FreeBSD @@ -228,81 +351,11 @@ Install SDL2 port if needed ```cc *.c -o ClassiCube -lgl -lSDL2``` -## Compiling - other +#### Other systems -#### Web +You'll have to write the necessary code. You should read `portability.md` in doc folder. -```emcc *.c -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_STACK=1Mb --js-library interop_web.js``` - -The generated javascript file has some issues. [See here for how to fix](doc/compile-fixes.md#webclient-patches) - -#### 3DS - -Run `make 3ds`. You'll need [libctru](https://github.com/devkitPro/libctru) - -**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `3ds-dev` group)** - -The 3DS port needs assistance from someone experienced with 3DS homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### Wii - -Run `make wii`. You'll need [libogc](https://github.com/devkitPro/libogc) - -**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `wii-dev` group)** - -The Wii port needs assistance from someone experienced with Wii homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### GameCube - -Run `make gamecube`. You'll need [libogc](https://github.com/devkitPro/libogc) - -**NOTE: It is highly recommended that you install the precompiled devkitpro packages from [here](https://devkitpro.org/wiki/Getting_Started) - you need the `gamecube-dev` group)** - -The GC port needs assistance from someone experienced with GameCube homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### PlayStation Portable - -Run `make psp`. You'll need [pspsdk](https://github.com/pspdev/pspsdk) - -**NOTE: It is recommended that you install the precompiled pspsdk version from [here](https://github.com/pspdev/pspdev/releases)** - -The PSP port needs assistance from someone experienced with PSP homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### PlayStation Vita - -Run `make vita`. You'll need [vitasdk](https://vitasdk.org/) - -The Vita port needs assistance from someone experienced with Vita homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### PlayStation 3 - -Run `make ps3`. You'll need [PSL1GHT](https://github.com/ps3dev/PSL1GHT) - -The PS3 port needs assistance from someone experienced with PS3 homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### Xbox - -Run `make xbox`. You'll need [nxdk](https://github.com/XboxDev/nxdk) - -The Xbox port needs assistance from someone experienced with Xbox homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### Dreamcast - -Run `make dreamcast`. You'll need [KallistiOS](https://github.com/KallistiOS/KallistiOS) - -The Dreamcast port needs assistance from someone experienced with Dreamcast homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -#### Nintendo 64 - -Run `make n64`. You'll need the opengl branch of [libdragon](https://github.com/DragonMinded/libdragon/tree/opengl) - -The Nintendo 64 port needs assistance from someone experienced with Nintendo 64 homebrew development - if you're interested, please get in contact with me. (`unknownshadow200` on Discord) - -You'll also need to stub out `WorkerLoop` function in `Http_Worker.c` for now - -##### Other - -You'll have to write the necessary code. You should read portability.md in doc folder. +
## Documentation @@ -325,7 +378,8 @@ Further information (e.g. style) for ClassiCube's source code can be found in th * To see help for a given built in command, type `/client help `. -## Open source technologies +
+

Open source technologies (click to expand)

* [curl](https://curl.se/) - HTTP/HTTPS for linux and macOS * [FreeType](https://www.freetype.org/) - Font handling for all platforms @@ -335,31 +389,41 @@ Further information (e.g. style) for ClassiCube's source code can be found in th * [Emscripten](https://emscripten.org/) - Compiles client for web * [RenderDoc](https://renderdoc.org/) - Graphics debugging * [BearSSL](https://www.bearssl.org/) - SSL/TLS support on consoles +* [libnx](https://github.com/switchbrew/libnx) - Backend for Switch +* [Ryujinx](https://github.com/Ryujinx/Ryujinx) - Emulator used to test Switch port +* [wut](https://github.com/devkitPro/wut/) - Backend for Wii U +* [Cemu](https://github.com/cemu-project/Cemu) - Emulator used to test Wii U port * [libctru](https://github.com/devkitPro/libctru) - Backend for 3DS * [citro3D](https://github.com/devkitPro/citro3d) - Rendering backend for 3DS * [Citra](https://github.com/citra-emu/citra) - Emulator used to test 3DS port -* [pspsdk](https://github.com/pspdev/pspsdk) - Backend for PSP -* [PPSSPP](https://github.com/hrydgard/ppsspp) - Emulator used to test PSP port -* [vitasdk](https://github.com/vitasdk) - Backend for PS Vita -* [Vita3K](https://github.com/Vita3K/Vita3K) - Emulator used to test Vita port -* [PSL1GHT](https://github.com/ps3dev/PSL1GHT) - Backend for PS3 -* [RPCS3](https://github.com/RPCS3/rpcs3) - Emulator used to test PS3 port * [libogc](https://github.com/devkitPro/libogc) - Backend for Wii and GameCube * [libfat](https://github.com/devkitPro/libfat) - Filesystem backend for Wii/GC * [Dolphin](https://github.com/dolphin-emu/dolphin) - Emulator used to test Wii/GC port -* [KallistiOS](https://github.com/KallistiOS/KallistiOS) - Backend for Dreamcast -* [GLdc](https://github.com/Kazade/GLdc) - Basis of rendering backend for Dreamcast -* [nullDC](https://github.com/skmp/nulldc) - Emulator used to test Dreamcast port -* [flycast](https://github.com/flyinghead/flycast) - Emulator used to test Dreamcast port +* [libdragon](https://github.com/DragonMinded/libdragon) - Backend for Nintendo 64 +* [ares](https://github.com/ares-emulator/ares) - Emulator used to test Nintendo 64 port +* [BlocksDS](https://github.com/blocksds/sdk) - Backend for Nintendo DS +* [melonDS](https://github.com/melonDS-emu/melonDS) - Emulator used to test Nintendo DS port +* [vitasdk](https://github.com/vitasdk) - Backend for PS Vita +* [Vita3K](https://github.com/Vita3K/Vita3K) - Emulator used to test Vita port +* [pspsdk](https://github.com/pspdev/pspsdk) - Backend for PSP +* [PPSSPP](https://github.com/hrydgard/ppsspp) - Emulator used to test PSP port +* [PSL1GHT](https://github.com/ps3dev/PSL1GHT) - Backend for PS3 +* [RPCS3](https://github.com/RPCS3/rpcs3) - Emulator used to test PS3 port +* [ps2sdk](https://github.com/ps2dev/ps2sdk) - Backend for PS2 +* [PCSX2](https://github.com/PCSX2/pcsx2) - Emulator used to test PS2 port +* [PSn00bSDK](https://github.com/Lameguy64/PSn00bSDK/) - Backend for PS1 +* [duckstation](https://github.com/stenzek/duckstation) - Emulator used to test PS1 port +* [libxenon](https://github.com/Free60Project/libxenon) - Backend for Xbox 360 * [nxdk](https://github.com/XboxDev/nxdk) - Backend for Xbox * [xemu](https://github.com/xemu-project/xemu) - Emulator used to test Xbox port * [cxbx-reloaded](https://github.com/Cxbx-Reloaded/Cxbx-Reloaded) - Emulator used to test Xbox port -* [libdragon](https://github.com/DragonMinded/libdragon) - Backend for Nintendo 64 -* [cen64](https://github.com/n64dev/cen64) - Emulator used to test Nintendo 64 port -* [ares](https://github.com/ares-emulator/ares) - Emulator used to test Nintendo 64 port -* [ps2sdk](https://github.com/ps2dev/ps2sdk) - Backend for PS2 -* [PCSX2](https://github.com/PCSX2/pcsx2) - Emulator used to test PS2 port +* [KallistiOS](https://github.com/KallistiOS/KallistiOS) - Backend for Dreamcast +* [GLdc](https://github.com/Kazade/GLdc) - Basis of rendering backend for Dreamcast +* [flycast](https://github.com/flyinghead/flycast) - Emulator used to test Dreamcast port +* [libyaul](https://github.com/yaul-org/libyaul) - Backend for Saturn +* [mednafen](https://mednafen.github.io/) - Emulator used to test Saturn port +
## Sound Credits ClassiCube uses sounds from [Freesound.org](https://freesound.org)
diff --git a/src/Animations.c b/src/Animations.c index 9c469dd64..e6da228c2 100644 --- a/src/Animations.c +++ b/src/Animations.c @@ -12,10 +12,17 @@ #include "Options.h" #include "Logger.h" -#define LIQUID_ANIM_MAX 64 +#ifdef CC_BUILD_ANIMATIONS +static void Animations_Update(int loc, struct Bitmap* bmp, int stride); + +#ifdef CC_BUILD_LOWMEM + #define LIQUID_ANIM_MAX 16 +#else + #define LIQUID_ANIM_MAX 64 +#endif + #define WATER_TEX_LOC 14 #define LAVA_TEX_LOC 30 -static void Animations_Update(int loc, struct Bitmap* bmp, int stride); #ifndef CC_BUILD_WEB /* Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt @@ -292,20 +299,20 @@ static void Animations_Clear(void) { } static void Animations_Validate(void) { - struct AnimationData data; + struct AnimationData* data; int maxX, maxY, tileX, tileY; int i, j; anims_validated = true; for (i = 0; i < anims_count; i++) { - data = anims_list[i]; + data = &anims_list[i]; - maxX = data.frameX + data.frameSize * data.statesCount; - maxY = data.frameY + data.frameSize; - tileX = Atlas2D_TileX(data.texLoc); - tileY = Atlas2D_TileY(data.texLoc); + maxX = data->frameX + data->frameSize * data->statesCount; + maxY = data->frameY + data->frameSize; + tileX = Atlas2D_TileX(data->texLoc); + tileY = Atlas2D_TileY(data->texLoc); - if (data.frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) { + if (data->frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) { Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY); } else if (maxX > anims_bmp.width || maxY > anims_bmp.height) { Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY); @@ -313,8 +320,8 @@ static void Animations_Validate(void) { /* if user has water/lava animations in their default.zip, disable built-in */ /* However, 'usewateranim' and 'uselavaanim' files should always disable use */ /* of custom water/lava animations, even when they exist in animations.png */ - if (data.texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false; - if (data.texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false; + if (data->texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false; + if (data->texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false; continue; } @@ -391,6 +398,10 @@ static void OnInit(void) { ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick); Event_Register_(&TextureEvents.PackChanged, NULL, OnPackChanged); } +#else +static void Animations_Clear(void) { } +static void OnInit(void) { } +#endif struct IGameComponent Animations_Component = { OnInit, /* Init */ diff --git a/src/Audio.c b/src/Audio.c index 509210c84..341264a04 100644 --- a/src/Audio.c +++ b/src/Audio.c @@ -12,11 +12,15 @@ #include "Stream.h" #include "Utils.h" #include "Options.h" +#include "Deflate.h" #ifdef CC_BUILD_ANDROID /* TODO: Refactor maybe to not rely on checking WinInfo.Handle != NULL */ #include "Window.h" #endif + int Audio_SoundsVolume, Audio_MusicVolume; +const cc_string Sounds_ZipPathMC = String_FromConst("audio/default.zip"); +const cc_string Sounds_ZipPathCC = String_FromConst("audio/classicube.zip"); static const cc_string audio_dir = String_FromConst("audio"); struct Sound { @@ -24,1169 +28,6 @@ struct Sound { void* data; cc_uint32 size; }; -static void ApplyVolume(cc_int16* samples, int count, int volume) { - int i; - - for (i = 0; i < (count & ~0x07); i += 8, samples += 8) { - samples[0] = (samples[0] * volume / 100); - samples[1] = (samples[1] * volume / 100); - samples[2] = (samples[2] * volume / 100); - samples[3] = (samples[3] * volume / 100); - - samples[4] = (samples[4] * volume / 100); - samples[5] = (samples[5] * volume / 100); - samples[6] = (samples[6] * volume / 100); - samples[7] = (samples[7] * volume / 100); - } - - for (; i < count; i++, samples++) { - samples[0] = (samples[0] * volume / 100); - } -} - -/* Common/Base methods */ -static void AudioWarn(cc_result res, const char* action) { - Logger_Warn(res, action, Audio_DescribeError); -} -static void AudioBase_Clear(struct AudioContext* ctx); -static cc_bool AudioBase_AdjustSound(struct AudioContext* ctx, struct AudioData* data); -static void AudioBase_AllocChunks(int size, void** chunks, int numChunks); -static void AudioBase_FreeChunks(void** chunks, int numChunks); - -/* achieve higher speed by playing samples at higher sample rate */ -#define Audio_AdjustSampleRate(data) ((data->sampleRate * data->rate) / 100) - -#if defined CC_BUILD_OPENAL -/*########################################################################################################################* -*------------------------------------------------------OpenAL backend-----------------------------------------------------* -*#########################################################################################################################*/ -/* Simpler to just include subset of OpenAL actually use here instead of including */ -/* === BEGIN OPENAL HEADERS === */ -#if defined _WIN32 -#define APIENTRY __cdecl -#else -#define APIENTRY -#endif -#define AL_NONE 0 -#define AL_SOURCE_STATE 0x1010 -#define AL_PLAYING 0x1012 -#define AL_BUFFERS_QUEUED 0x1015 -#define AL_BUFFERS_PROCESSED 0x1016 -#define AL_FORMAT_MONO16 0x1101 -#define AL_FORMAT_STEREO16 0x1103 - -#define AL_INVALID_NAME 0xA001 -#define AL_INVALID_ENUM 0xA002 -#define AL_INVALID_VALUE 0xA003 -#define AL_INVALID_OPERATION 0xA004 -#define AL_OUT_OF_MEMORY 0xA005 - -typedef char ALboolean; -typedef int ALint; -typedef unsigned int ALuint; -typedef int ALsizei; -typedef int ALenum; - -/* Apologies for the ugly dynamic symbol definitions here */ -static ALenum (APIENTRY *_alGetError)(void); -static void (APIENTRY *_alGenSources)(ALsizei n, ALuint* sources); -static void (APIENTRY *_alDeleteSources)(ALsizei n, const ALuint* sources); -static void (APIENTRY *_alGetSourcei)(ALuint source, ALenum param, ALint* value); -static void (APIENTRY *_alSourcePlay)(ALuint source); -static void (APIENTRY *_alSourceStop)(ALuint source); -static void (APIENTRY *_alSourceQueueBuffers)(ALuint source, ALsizei nb, const ALuint* buffers); -static void (APIENTRY *_alSourceUnqueueBuffers)(ALuint source, ALsizei nb, ALuint* buffers); -static void (APIENTRY *_alGenBuffers)(ALsizei n, ALuint* buffers); -static void (APIENTRY *_alDeleteBuffers)(ALsizei n, const ALuint* buffers); -static void (APIENTRY *_alBufferData)(ALuint buffer, ALenum format, const void* data, ALsizei size, ALsizei freq); - -static void (APIENTRY *_alDistanceModel)(ALenum distanceModel); -static void* (APIENTRY *_alcCreateContext)(void* device, const ALint* attrlist); -static ALboolean (APIENTRY *_alcMakeContextCurrent)(void* context); -static void (APIENTRY *_alcDestroyContext)(void* context); -static void* (APIENTRY *_alcOpenDevice)(const char* devicename); -static ALboolean (APIENTRY *_alcCloseDevice)(void* device); -static ALenum (APIENTRY *_alcGetError)(void* device); -/* === END OPENAL HEADERS === */ - -struct AudioContext { - ALuint source; - ALuint buffers[AUDIO_MAX_BUFFERS]; - ALuint freeIDs[AUDIO_MAX_BUFFERS]; - int count, free, sampleRate; - ALenum channels; - cc_uint32 _tmpSize; void* _tmpData; -}; -static void* audio_device; -static void* audio_context; -#define AUDIO_HAS_BACKEND - -#if defined CC_BUILD_WIN -static const cc_string alLib = String_FromConst("openal32.dll"); -#elif defined CC_BUILD_MACOS -static const cc_string alLib = String_FromConst("/System/Library/Frameworks/OpenAL.framework/Versions/A/OpenAL"); -#elif defined CC_BUILD_IOS -static const cc_string alLib = String_FromConst("/System/Library/Frameworks/OpenAL.framework/OpenAL"); -#elif defined CC_BUILD_NETBSD -static const cc_string alLib = String_FromConst("/usr/pkg/lib/libopenal.so"); -#elif defined CC_BUILD_BSD -static const cc_string alLib = String_FromConst("libopenal.so"); -#else -static const cc_string alLib = String_FromConst("libopenal.so.1"); -#endif - -static cc_bool LoadALFuncs(void) { - static const struct DynamicLibSym funcs[] = { - DynamicLib_Sym(alcCreateContext), DynamicLib_Sym(alcMakeContextCurrent), - DynamicLib_Sym(alcDestroyContext), DynamicLib_Sym(alcOpenDevice), - DynamicLib_Sym(alcCloseDevice), DynamicLib_Sym(alcGetError), - - DynamicLib_Sym(alGetError), DynamicLib_Sym(alGenSources), - DynamicLib_Sym(alDeleteSources), DynamicLib_Sym(alGetSourcei), - DynamicLib_Sym(alSourcePlay), DynamicLib_Sym(alSourceStop), - DynamicLib_Sym(alSourceQueueBuffers), DynamicLib_Sym(alSourceUnqueueBuffers), - DynamicLib_Sym(alGenBuffers), DynamicLib_Sym(alDeleteBuffers), - DynamicLib_Sym(alBufferData), DynamicLib_Sym(alDistanceModel) - }; - void* lib; - - return DynamicLib_LoadAll(&alLib, funcs, Array_Elems(funcs), &lib); -} - -static cc_result CreateALContext(void) { - ALenum err; - audio_device = _alcOpenDevice(NULL); - if ((err = _alcGetError(audio_device))) return err; - if (!audio_device) return AL_ERR_INIT_DEVICE; - - audio_context = _alcCreateContext(audio_device, NULL); - if ((err = _alcGetError(audio_device))) return err; - if (!audio_context) return AL_ERR_INIT_CONTEXT; - - _alcMakeContextCurrent(audio_context); - return _alcGetError(audio_device); -} - -static cc_bool AudioBackend_Init(void) { - static const cc_string msg = String_FromConst("Failed to init OpenAL. No audio will play."); - cc_result res; - if (audio_device) return true; - if (!LoadALFuncs()) { Logger_WarnFunc(&msg); return false; } - - res = CreateALContext(); - if (res) { AudioWarn(res, "initing OpenAL"); return false; } - return true; -} - -static void AudioBackend_Free(void) { - if (!audio_device) return; - _alcMakeContextCurrent(NULL); - - if (audio_context) _alcDestroyContext(audio_context); - if (audio_device) _alcCloseDevice(audio_device); - - audio_context = NULL; - audio_device = NULL; -} - -static void ClearFree(struct AudioContext* ctx) { - int i; - for (i = 0; i < AUDIO_MAX_BUFFERS; i++) { - ctx->freeIDs[i] = 0; - } - ctx->free = 0; -} - -void Audio_Init(struct AudioContext* ctx, int buffers) { - _alDistanceModel(AL_NONE); - ClearFree(ctx); - - ctx->source = 0; - ctx->count = buffers; -} - -static void Audio_Stop(struct AudioContext* ctx) { - _alSourceStop(ctx->source); -} - -static void Audio_Reset(struct AudioContext* ctx) { - _alDeleteSources(1, &ctx->source); - _alDeleteBuffers(ctx->count, ctx->buffers); - ctx->source = 0; -} - -void Audio_Close(struct AudioContext* ctx) { - if (ctx->source) { - Audio_Stop(ctx); - Audio_Reset(ctx); - _alGetError(); /* Reset error state */ - } - ClearFree(ctx); - AudioBase_Clear(ctx); -} - -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate) { - ALenum i, err; - if (!ctx->source) { - _alGetError(); /* Reset error state */ - _alGenSources(1, &ctx->source); - if ((err = _alGetError())) return err; - - _alGenBuffers(ctx->count, ctx->buffers); - if ((err = _alGetError())) return err; - - for (i = 0; i < ctx->count; i++) { - ctx->freeIDs[i] = ctx->buffers[i]; - } - ctx->free = ctx->count; - } - ctx->sampleRate = sampleRate; - - if (channels == 1) { - ctx->channels = AL_FORMAT_MONO16; - } else if (channels == 2) { - ctx->channels = AL_FORMAT_STEREO16; - } else { - return ERR_INVALID_ARGUMENT; - } - return 0; -} - -cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { - ALuint buffer; - ALenum err; - - if (!ctx->free) return ERR_INVALID_ARGUMENT; - buffer = ctx->freeIDs[--ctx->free]; - _alGetError(); /* Reset error state */ - - _alBufferData(buffer, ctx->channels, chunk, size, ctx->sampleRate); - if ((err = _alGetError())) return err; - _alSourceQueueBuffers(ctx->source, 1, &buffer); - if ((err = _alGetError())) return err; - return 0; -} - -cc_result Audio_Play(struct AudioContext* ctx) { - _alSourcePlay(ctx->source); - return _alGetError(); -} - -cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - ALint processed = 0; - ALuint buffer; - ALenum err; - - *inUse = 0; - if (!ctx->source) return 0; - - _alGetError(); /* Reset error state */ - _alGetSourcei(ctx->source, AL_BUFFERS_PROCESSED, &processed); - if ((err = _alGetError())) return err; - - if (processed > 0) { - _alSourceUnqueueBuffers(ctx->source, 1, &buffer); - if ((err = _alGetError())) return err; - - ctx->freeIDs[ctx->free++] = buffer; - } - *inUse = ctx->count - ctx->free; return 0; -} - -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { - /* Channels/Sample rate is per buffer, not a per source property */ - return true; -} - -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data) { - cc_bool ok = AudioBase_AdjustSound(ctx, data); - cc_result res; - if (!ok) return ERR_OUT_OF_MEMORY; - data->sampleRate = Audio_AdjustSampleRate(data); - - if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate))) return res; - if ((res = Audio_QueueChunk(ctx, data->data, data->size))) return res; - if ((res = Audio_Play(ctx))) return res; - return 0; -} - -static const char* GetError(cc_result res) { - switch (res) { - case AL_ERR_INIT_CONTEXT: return "Failed to init OpenAL context"; - case AL_ERR_INIT_DEVICE: return "Failed to init OpenAL device"; - case AL_INVALID_NAME: return "Invalid parameter name"; - case AL_INVALID_ENUM: return "Invalid parameter"; - case AL_INVALID_VALUE: return "Invalid parameter value"; - case AL_INVALID_OPERATION: return "Invalid operation"; - case AL_OUT_OF_MEMORY: return "OpenAL out of memory"; - } - return NULL; -} - -cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { - const char* err = GetError(res); - if (err) String_AppendConst(dst, err); - return err != NULL; -} - -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { - AudioBase_AllocChunks(size, chunks, numChunks); -} - -void Audio_FreeChunks(void** chunks, int numChunks) { - AudioBase_FreeChunks(chunks, numChunks); -} -#elif defined CC_BUILD_WINMM -/*########################################################################################################################* -*------------------------------------------------------WinMM backend------------------------------------------------------* -*#########################################################################################################################*/ -#define WIN32_LEAN_AND_MEAN -#define NOSERVICE -#define NOMCX -#define NOIME -#ifndef UNICODE -#define UNICODE -#define _UNICODE -#endif -#include - -/* === BEGIN mmsyscom.h === */ -#define CALLBACK_NULL 0x00000000l -typedef UINT MMRESULT; -#define WINMMAPI DECLSPEC_IMPORT -#define MMSYSERR_BADDEVICEID 2 -/* === BEGIN mmeapi.h === */ -typedef struct WAVEHDR_ { - LPSTR lpData; - DWORD dwBufferLength; - DWORD dwBytesRecorded; - DWORD_PTR dwUser; - DWORD dwFlags; - DWORD dwLoops; - struct WAVEHDR_* lpNext; - DWORD_PTR reserved; -} WAVEHDR; - -typedef struct WAVEFORMATEX_ { - WORD wFormatTag; - WORD nChannels; - DWORD nSamplesPerSec; - DWORD nAvgBytesPerSec; - WORD nBlockAlign; - WORD wBitsPerSample; - WORD cbSize; -} WAVEFORMATEX; -typedef void* HWAVEOUT; - -#define WAVE_MAPPER ((UINT)-1) -#define WAVE_FORMAT_PCM 1 -#define WHDR_DONE 0x00000001 -#define WHDR_PREPARED 0x00000002 - -WINMMAPI MMRESULT WINAPI waveOutOpen(HWAVEOUT* phwo, UINT deviceID, const WAVEFORMATEX* fmt, DWORD_PTR callback, DWORD_PTR instance, DWORD flags); -WINMMAPI MMRESULT WINAPI waveOutClose(HWAVEOUT hwo); -WINMMAPI MMRESULT WINAPI waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* hdr, UINT hdrSize); -WINMMAPI MMRESULT WINAPI waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* hdr, UINT hdrSize); -WINMMAPI MMRESULT WINAPI waveOutWrite(HWAVEOUT hwo, WAVEHDR* hdr, UINT hdrSize); -WINMMAPI MMRESULT WINAPI waveOutReset(HWAVEOUT hwo); -WINMMAPI MMRESULT WINAPI waveOutGetErrorTextA(MMRESULT err, LPSTR text, UINT textLen); -WINMMAPI UINT WINAPI waveOutGetNumDevs(void); -/* === END mmeapi.h === */ - -struct AudioContext { - HWAVEOUT handle; - WAVEHDR headers[AUDIO_MAX_BUFFERS]; - int count, channels, sampleRate; - cc_uint32 _tmpSize; void* _tmpData; -}; -static cc_bool AudioBackend_Init(void) { return true; } -static void AudioBackend_Free(void) { } -#define AUDIO_HAS_BACKEND - -void Audio_Init(struct AudioContext* ctx, int buffers) { - int i; - for (i = 0; i < buffers; i++) { - ctx->headers[i].dwFlags = WHDR_DONE; - } - ctx->count = buffers; -} - -static void Audio_Stop(struct AudioContext* ctx) { - waveOutReset(ctx->handle); -} - -static cc_result Audio_Reset(struct AudioContext* ctx) { - cc_result res; - if (!ctx->handle) return 0; - - res = waveOutClose(ctx->handle); - ctx->handle = NULL; - return res; -} - -void Audio_Close(struct AudioContext* ctx) { - int inUse; - if (ctx->handle) { - Audio_Stop(ctx); - Audio_Poll(ctx, &inUse); /* unprepare buffers */ - Audio_Reset(ctx); - } - AudioBase_Clear(ctx); -} - -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate) { - WAVEFORMATEX fmt; - cc_result res; - int sampleSize; - - if (ctx->channels == channels && ctx->sampleRate == sampleRate) return 0; - ctx->channels = channels; - ctx->sampleRate = sampleRate; - - sampleSize = channels * 2; /* 16 bits per sample / 8 */ - if ((res = Audio_Reset(ctx))) return res; - - fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.nChannels = channels; - fmt.nSamplesPerSec = sampleRate; - fmt.nAvgBytesPerSec = sampleRate * sampleSize; - fmt.nBlockAlign = sampleSize; - fmt.wBitsPerSample = 16; - fmt.cbSize = 0; - - res = waveOutOpen(&ctx->handle, WAVE_MAPPER, &fmt, 0, 0, CALLBACK_NULL); - /* Show a better error message when no audio output devices connected than */ - /* "A device ID has been used that is out of range for your system" */ - if (res == MMSYSERR_BADDEVICEID && waveOutGetNumDevs() == 0) - return ERR_NO_AUDIO_OUTPUT; - return res; -} - -cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { - cc_result res = 0; - WAVEHDR* hdr; - int i; - - for (i = 0; i < ctx->count; i++) { - hdr = &ctx->headers[i]; - if (!(hdr->dwFlags & WHDR_DONE)) continue; - - Mem_Set(hdr, 0, sizeof(WAVEHDR)); - hdr->lpData = (LPSTR)chunk; - hdr->dwBufferLength = dataSize; - hdr->dwLoops = 1; - - if ((res = waveOutPrepareHeader(ctx->handle, hdr, sizeof(WAVEHDR)))) return res; - if ((res = waveOutWrite(ctx->handle, hdr, sizeof(WAVEHDR)))) return res; - return 0; - } - /* tried to queue data without polling for free buffers first */ - return ERR_INVALID_ARGUMENT; -} - -cc_result Audio_Play(struct AudioContext* ctx) { return 0; } - -cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - cc_result res = 0; - WAVEHDR* hdr; - int i, count = 0; - - for (i = 0; i < ctx->count; i++) { - hdr = &ctx->headers[i]; - if (!(hdr->dwFlags & WHDR_DONE)) { count++; continue; } - - if (!(hdr->dwFlags & WHDR_PREPARED)) continue; - /* unprepare this WAVEHDR so it can be reused */ - res = waveOutUnprepareHeader(ctx->handle, hdr, sizeof(WAVEHDR)); - if (res) break; - } - - *inUse = count; return res; -} - - -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { - int channels = data->channels; - int sampleRate = Audio_AdjustSampleRate(data); - return !ctx->channels || (ctx->channels == channels && ctx->sampleRate == sampleRate); -} - -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data) { - cc_bool ok = AudioBase_AdjustSound(ctx, data); - cc_result res; - if (!ok) return ERR_OUT_OF_MEMORY; - data->sampleRate = Audio_AdjustSampleRate(data); - - if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate))) return res; - if ((res = Audio_QueueChunk(ctx, data->data, data->size))) return res; - return 0; -} - -cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { - char buffer[NATIVE_STR_LEN] = { 0 }; - waveOutGetErrorTextA(res, buffer, NATIVE_STR_LEN); - - if (!buffer[0]) return false; - String_AppendConst(dst, buffer); - return true; -} - -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { - AudioBase_AllocChunks(size, chunks, numChunks); -} - -void Audio_FreeChunks(void** chunks, int numChunks) { - AudioBase_FreeChunks(chunks, numChunks); -} -#elif defined CC_BUILD_OPENSLES -/*########################################################################################################################* -*----------------------------------------------------OpenSL ES backend----------------------------------------------------* -*#########################################################################################################################*/ -#include -#include -static SLObjectItf slEngineObject; -static SLEngineItf slEngineEngine; -static SLObjectItf slOutputObject; -#define AUDIO_HAS_BACKEND - -struct AudioContext { - int count, channels, sampleRate; - SLObjectItf playerObject; - SLPlayItf playerPlayer; - SLBufferQueueItf playerQueue; - SLPlaybackRateItf playerRate; - cc_uint32 _tmpSize; void* _tmpData; -}; - -static SLresult (SLAPIENTRY *_slCreateEngine)(SLObjectItf* engine, SLuint32 numOptions, const SLEngineOption* engineOptions, - SLuint32 numInterfaces, const SLInterfaceID* interfaceIds, const SLboolean* interfaceRequired); -static SLInterfaceID* _SL_IID_NULL; -static SLInterfaceID* _SL_IID_PLAY; -static SLInterfaceID* _SL_IID_ENGINE; -static SLInterfaceID* _SL_IID_BUFFERQUEUE; -static SLInterfaceID* _SL_IID_PLAYBACKRATE; -static const cc_string slLib = String_FromConst("libOpenSLES.so"); - -static cc_bool LoadSLFuncs(void) { - static const struct DynamicLibSym funcs[] = { - DynamicLib_Sym(slCreateEngine), DynamicLib_Sym(SL_IID_NULL), - DynamicLib_Sym(SL_IID_PLAY), DynamicLib_Sym(SL_IID_ENGINE), - DynamicLib_Sym(SL_IID_BUFFERQUEUE), DynamicLib_Sym(SL_IID_PLAYBACKRATE) - }; - void* lib; - - return DynamicLib_LoadAll(&slLib, funcs, Array_Elems(funcs), &lib); -} - -static cc_bool AudioBackend_Init(void) { - static const cc_string msg = String_FromConst("Failed to init OpenSLES. No audio will play."); - SLInterfaceID ids[1]; - SLboolean req[1]; - SLresult res; - - if (slEngineObject) return true; - if (!LoadSLFuncs()) { Logger_WarnFunc(&msg); return false; } - - /* mixer doesn't use any effects */ - ids[0] = *_SL_IID_NULL; - req[0] = SL_BOOLEAN_FALSE; - - res = _slCreateEngine(&slEngineObject, 0, NULL, 0, NULL, NULL); - if (res) { AudioWarn(res, "creating OpenSL ES engine"); return false; } - - res = (*slEngineObject)->Realize(slEngineObject, SL_BOOLEAN_FALSE); - if (res) { AudioWarn(res, "realising OpenSL ES engine"); return false; } - - res = (*slEngineObject)->GetInterface(slEngineObject, *_SL_IID_ENGINE, &slEngineEngine); - if (res) { AudioWarn(res, "initing OpenSL ES engine"); return false; } - - res = (*slEngineEngine)->CreateOutputMix(slEngineEngine, &slOutputObject, 1, ids, req); - if (res) { AudioWarn(res, "creating OpenSL ES mixer"); return false; } - - res = (*slOutputObject)->Realize(slOutputObject, SL_BOOLEAN_FALSE); - if (res) { AudioWarn(res, "realising OpenSL ES mixer"); return false; } - - return true; -} - -static void AudioBackend_Free(void) { - if (slOutputObject) { - (*slOutputObject)->Destroy(slOutputObject); - slOutputObject = NULL; - } - if (slEngineObject) { - (*slEngineObject)->Destroy(slEngineObject); - slEngineObject = NULL; - slEngineEngine = NULL; - } -} - -void Audio_Init(struct AudioContext* ctx, int buffers) { - ctx->count = buffers; -} - -static void Audio_Stop(struct AudioContext* ctx) { - if (!ctx->playerPlayer) return; - - (*ctx->playerQueue)->Clear(ctx->playerQueue); - (*ctx->playerPlayer)->SetPlayState(ctx->playerPlayer, SL_PLAYSTATE_STOPPED); -} - -static void Audio_Reset(struct AudioContext* ctx) { - SLObjectItf playerObject = ctx->playerObject; - if (!playerObject) return; - - (*playerObject)->Destroy(playerObject); - ctx->playerObject = NULL; - ctx->playerPlayer = NULL; - ctx->playerQueue = NULL; - ctx->playerRate = NULL; -} - -void Audio_Close(struct AudioContext* ctx) { - Audio_Stop(ctx); - Audio_Reset(ctx); - AudioBase_Clear(ctx); -} - -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate) { - SLDataLocator_AndroidSimpleBufferQueue input; - SLDataLocator_OutputMix output; - SLObjectItf playerObject; - SLDataFormat_PCM fmt; - SLInterfaceID ids[3]; - SLboolean req[3]; - SLDataSource src; - SLDataSink dst; - cc_result res; - - if (ctx->channels == channels && ctx->sampleRate == sampleRate) return 0; - ctx->channels = channels; - ctx->sampleRate = sampleRate; - Audio_Reset(ctx); - - fmt.formatType = SL_DATAFORMAT_PCM; - fmt.numChannels = channels; - fmt.samplesPerSec = sampleRate * 1000; - fmt.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - fmt.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - fmt.channelMask = 0; - fmt.endianness = SL_BYTEORDER_LITTLEENDIAN; - - input.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - input.numBuffers = ctx->count; - output.locatorType = SL_DATALOCATOR_OUTPUTMIX; - output.outputMix = slOutputObject; - - src.pLocator = &input; - src.pFormat = &fmt; - dst.pLocator = &output; - dst.pFormat = NULL; - - ids[0] = *_SL_IID_BUFFERQUEUE; req[0] = SL_BOOLEAN_TRUE; - ids[1] = *_SL_IID_PLAY; req[1] = SL_BOOLEAN_TRUE; - ids[2] = *_SL_IID_PLAYBACKRATE; req[2] = SL_BOOLEAN_TRUE; - - res = (*slEngineEngine)->CreateAudioPlayer(slEngineEngine, &playerObject, &src, &dst, 3, ids, req); - ctx->playerObject = playerObject; - if (res) return res; - - if ((res = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE))) return res; - if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_PLAY, &ctx->playerPlayer))) return res; - if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_BUFFERQUEUE, &ctx->playerQueue))) return res; - if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_PLAYBACKRATE, &ctx->playerRate))) return res; - return 0; -} - -cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { - return (*ctx->playerQueue)->Enqueue(ctx->playerQueue, chunk, size); -} - -static cc_result Audio_Pause(struct AudioContext* ctx) { - return (*ctx->playerPlayer)->SetPlayState(ctx->playerPlayer, SL_PLAYSTATE_PAUSED); -} - -cc_result Audio_Play(struct AudioContext* ctx) { - return (*ctx->playerPlayer)->SetPlayState(ctx->playerPlayer, SL_PLAYSTATE_PLAYING); -} - -cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - SLBufferQueueState state = { 0 }; - cc_result res = 0; - - if (ctx->playerQueue) { - res = (*ctx->playerQueue)->GetState(ctx->playerQueue, &state); - } - *inUse = state.count; - return res; -} - -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { - return !ctx->channels || (ctx->channels == data->channels && ctx->sampleRate == data->sampleRate); -} - -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data) { - cc_bool ok = AudioBase_AdjustSound(ctx, data); - cc_result res; - if (!ok) return ERR_OUT_OF_MEMORY; - - if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate))) return res; - /* rate is in milli, so 1000 = normal rate */ - if ((res = (*ctx->playerRate)->SetRate(ctx->playerRate, data->rate * 10))) return res; - - if ((res = Audio_QueueChunk(ctx, data->data, data->size))) return res; - if ((res = Audio_Play(ctx))) return res; - return 0; -} - -static const char* GetError(cc_result res) { - switch (res) { - case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; - case SL_RESULT_PARAMETER_INVALID: return "Invalid parameter"; - case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; - case SL_RESULT_RESOURCE_ERROR: return "Resource error"; - case SL_RESULT_RESOURCE_LOST: return "Resource lost"; - case SL_RESULT_IO_ERROR: return "I/O error"; - case SL_RESULT_BUFFER_INSUFFICIENT: return "Insufficient buffer"; - case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; - case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; - case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; - case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; - case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; - case SL_RESULT_INTERNAL_ERROR: return "Internal error"; - case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; - case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; - case SL_RESULT_CONTROL_LOST: return "Control lost"; - } - return NULL; -} - -cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { - const char* err = GetError(res); - if (err) String_AppendConst(dst, err); - return err != NULL; -} - -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { - AudioBase_AllocChunks(size, chunks, numChunks); -} - -void Audio_FreeChunks(void** chunks, int numChunks) { - AudioBase_FreeChunks(chunks, numChunks); -} -#elif defined CC_BUILD_3DS -/*########################################################################################################################* -*-------------------------------------------------------3DS backend-------------------------------------------------------* -*#########################################################################################################################*/ -#include <3ds.h> -struct AudioContext { - int chanID, used; - ndspWaveBuf bufs[AUDIO_MAX_BUFFERS]; - int count, channels, sampleRate; - void* _tmpData; int _tmpSize; - cc_bool stereo; -}; -static int channelIDs; - -static cc_bool AudioBackend_Init(void) { - int result = ndspInit(); - Platform_Log1("NDSP_INIT: %i", &result); - ndspSetOutputMode(NDSP_OUTPUT_STEREO); - return result == 0; -} -static void AudioBackend_Free(void) { } -#define AUDIO_HAS_BACKEND - -void Audio_Init(struct AudioContext* ctx, int buffers) { - int chanID = -1; - - for (int i = 0; i < 24; i++) - { - // channel in use - if (channelIDs & (1 << i)) continue; - - chanID = i; break; - } - if (chanID == -1) return; - - channelIDs |= (1 << chanID); - ctx->count = buffers; - ctx->chanID = chanID; - ctx->used = true; - - ndspChnSetInterp(ctx->chanID, NDSP_INTERP_LINEAR); -} - -void Audio_Close(struct AudioContext* ctx) { - if (ctx->used) { - ndspChnWaveBufClear(ctx->chanID); - ctx->channels &= ~(1 << ctx->chanID); - channelIDs &= ~(1 << ctx->chanID); - } - - ctx->used = false; - AudioBase_Clear(ctx); -} - -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate) { - ctx->stereo = (channels == 2); - int fmt = ctx->stereo ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16; - - ndspChnSetFormat(ctx->chanID, fmt); - ndspChnSetRate(ctx->chanID, sampleRate); - return 0; -} - -cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { - ndspWaveBuf* buf; - - // DSP audio buffers must be aligned to a multiple of 0x80, according to the example code I could find. - if (((uintptr_t)chunk & 0x7F) != 0) { - Platform_Log1("Audio_QueueData: tried to queue buffer with non-aligned audio buffer 0x%x\n", &chunk); - } - if ((dataSize & 0x7F) != 0) { - Platform_Log1("Audio_QueueData: unaligned audio data size 0x%x\n", &dataSize); - } - - for (int i = 0; i < ctx->count; i++) - { - buf = &ctx->bufs[i]; - //Platform_Log2("QUEUE_CHUNK: %i = %i", &ctx->chanID, &buf->status); - if (buf->status == NDSP_WBUF_QUEUED || buf->status == NDSP_WBUF_PLAYING) - continue; - - buf->data_pcm16 = chunk; - buf->nsamples = dataSize / (sizeof(cc_int16) * (ctx->stereo ? 2 : 1)); - //Platform_Log1("PLAYING ON: %i", &ctx->chanID); - DSP_FlushDataCache(buf->data_pcm16, dataSize); - ndspChnWaveBufAdd(ctx->chanID, buf); - return 0; - } - // tried to queue data without polling for free buffers first - return ERR_INVALID_ARGUMENT; -} - -cc_result Audio_Play(struct AudioContext* ctx) { return 0; } - -cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - ndspWaveBuf* buf; - int count = 0; - - for (int i = 0; i < ctx->count; i++) - { - buf = &ctx->bufs[i]; - //Platform_Log2("CHECK_CHUNK: %i = %i", &ctx->chanID, &buf->status); - if (buf->status == NDSP_WBUF_QUEUED || buf->status == NDSP_WBUF_PLAYING) { - count++; continue; - } - } - - *inUse = count; - return 0; -} - - -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { - return true; -} - -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data) { - float mix[12] = { 0 }; - mix[0] = data->volume / 100.0f; - mix[1] = data->volume / 100.0f; - - ndspChnSetMix(ctx->chanID, mix); - data->sampleRate = Audio_AdjustSampleRate(data); - cc_result res; - - if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate))) return res; - if ((res = Audio_QueueChunk(ctx, data->data, data->size))) return res; - return 0; -} - -cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { - return false; -} - -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { - size = (size + 0x7F) & ~0x7F; // round up to nearest multiple of 0x80 - cc_uint8* dst = linearAlloc(size * numChunks); - for (int i = 0; i < numChunks; i++) { - chunks[i] = dst ? (dst + size * i) : NULL; - } -} - -void Audio_FreeChunks(void** chunks, int numChunks) { - linearFree(chunks[0]); -} -#elif defined CC_BUILD_DREAMCAST -/*########################################################################################################################* -*----------------------------------------------------Dreamcast backend----------------------------------------------------* -*#########################################################################################################################*/ -#include -/* TODO needs way more testing, especially with sounds */ - -struct AudioBuffer { - int available; - int bytesLeft; - void* samples; -}; - -struct AudioContext { - int used, bufHead, channels; - snd_stream_hnd_t hnd; - struct AudioBuffer bufs[AUDIO_MAX_BUFFERS]; - int count, sampleRate; - void* _tmpData; int _tmpSize; -}; - -static cc_bool AudioBackend_Init(void) { - return snd_stream_init() == 0; -} - -static void AudioBackend_Free(void) { - snd_stream_shutdown(); -} -#define AUDIO_HAS_BACKEND - -static void* AudioCallback(snd_stream_hnd_t hnd, int smp_req, int *smp_recv) { - struct AudioContext* ctx = snd_stream_get_userdata(hnd); - struct AudioBuffer* buf = &ctx->bufs[ctx->bufHead]; - - int samples = min(buf->bytesLeft, smp_req); - *smp_recv = samples; - void* ptr = buf->samples; - - buf->samples += samples; - buf->bytesLeft -= samples; - - if (buf->bytesLeft == 0) { - ctx->bufHead = (ctx->bufHead + 1) % ctx->count; - buf->samples = NULL; - buf->available = true; - } - return ptr; -} - -void Audio_Init(struct AudioContext* ctx, int buffers) { - ctx->hnd = snd_stream_alloc(AudioCallback, SND_STREAM_BUFFER_MAX); - if (ctx->hnd == SND_STREAM_INVALID) return; - snd_stream_set_userdata(ctx->hnd, ctx); - - Mem_Set(ctx->bufs, 0, sizeof(ctx->bufs)); - for (int i = 0; i < buffers; i++) { - ctx->bufs[i].available = true; - } - - ctx->count = buffers; - ctx->used = true; - ctx->bufHead = 0; -} - -void Audio_Close(struct AudioContext* ctx) { - if (ctx->used) { - snd_stream_stop(ctx->hnd); - snd_stream_destroy(ctx->hnd); - } - - ctx->used = false; - ctx->hnd = SND_STREAM_INVALID; - AudioBase_Clear(ctx); -} - -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate) { - ctx->channels = channels; - ctx->sampleRate = sampleRate; - return 0; -} - -cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { - struct AudioBuffer* buf; - - for (int i = 0; i < ctx->count; i++) - { - buf = &ctx->bufs[i]; - if (!buf->available) continue; - - buf->samples = chunk; - buf->bytesLeft = dataSize; - buf->available = false; - return 0; - } - // tried to queue data without polling for free buffers first - return ERR_INVALID_ARGUMENT; -} - -cc_result Audio_Play(struct AudioContext* ctx) { - snd_stream_start(ctx->hnd, ctx->sampleRate, ctx->channels == 2); - return 0; -} - -cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - struct AudioBuffer* buf; - snd_stream_poll(ctx->hnd); - int count = 0; - - for (int i = 0; i < ctx->count; i++) - { - buf = &ctx->bufs[i]; - if (!buf->available) count++; - } - - *inUse = count; - return 0; -} - -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { - return true; -} - -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data) { - snd_stream_volume(ctx->hnd, data->volume); - data->sampleRate = Audio_AdjustSampleRate(data); - cc_result res; - - if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate))) return res; - if ((res = Audio_QueueChunk(ctx, data->data, data->size))) return res; - if ((res = Audio_Play(ctx))) return res; - return 0; -} - -cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { - return false; -} - -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { - AudioBase_AllocChunks(size, chunks, numChunks); -} - -void Audio_FreeChunks(void** chunks, int numChunks) { - AudioBase_FreeChunks(chunks, numChunks); -} - -#elif defined CC_BUILD_WEBAUDIO -/*########################################################################################################################* -*-----------------------------------------------------WebAudio backend----------------------------------------------------* -*#########################################################################################################################*/ -struct AudioContext { int contextID; }; -extern int interop_InitAudio(void); -extern int interop_AudioCreate(void); -extern void interop_AudioClose(int contextID); -extern int interop_AudioPlay(int contextID, const void* name, int volume, int rate); -extern int interop_AudioPoll(int contetID, int* inUse); -extern int interop_AudioDescribe(int res, char* buffer, int bufferLen); - -static cc_bool AudioBackend_Init(void) { - cc_result res = interop_InitAudio(); - if (res) { AudioWarn(res, "initing WebAudio context"); return false; } - return true; -} - -void Audio_Init(struct AudioContext* ctx, int buffers) { } -void Audio_Close(struct AudioContext* ctx) { - if (ctx->contextID) interop_AudioClose(ctx->contextID); - ctx->contextID = 0; -} - -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate) { - return ERR_NOT_SUPPORTED; -} -cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { - return ERR_NOT_SUPPORTED; -} -cc_result Audio_Play(struct AudioContext* ctx) { return ERR_NOT_SUPPORTED; } - -cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - if (ctx->contextID) - return interop_AudioPoll(ctx->contextID, inUse); - - *inUse = 0; return 0; -} - -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { - /* Channels/Sample rate is per buffer, not a per source property */ - return true; -} - -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data) { - if (!ctx->contextID) - ctx->contextID = interop_AudioCreate(); - return interop_AudioPlay(ctx->contextID, data->data, data->volume, data->rate); -} - -cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { - char buffer[NATIVE_STR_LEN]; - int len = interop_AudioDescribe(res, buffer, NATIVE_STR_LEN); - - String_AppendUtf8(dst, buffer, len); - return len > 0; -} - -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { - AudioBase_AllocChunks(size, chunks, numChunks); -} - -void Audio_FreeChunks(void** chunks, int numChunks) { - AudioBase_FreeChunks(chunks, numChunks); -} -#endif - -/*########################################################################################################################* -*---------------------------------------------------Common backend code---------------------------------------------------* -*#########################################################################################################################*/ -#ifndef AUDIO_HAS_BACKEND -static void AudioBackend_Free(void) { } -#else -static void AudioBase_Clear(struct AudioContext* ctx) { - ctx->count = 0; - ctx->channels = 0; - ctx->sampleRate = 0; - - Mem_Free(ctx->_tmpData); - ctx->_tmpData = NULL; - ctx->_tmpSize = 0; -} - -static cc_bool AudioBase_AdjustSound(struct AudioContext* ctx, struct AudioData* data) { - void* audio; - if (data->volume >= 100) return true; - - /* copy to temp buffer to apply volume */ - if (ctx->_tmpSize < data->size) { - /* TODO: check if we can realloc NULL without a problem */ - if (ctx->_tmpData) { - audio = Mem_TryRealloc(ctx->_tmpData, data->size, 1); - } else { - audio = Mem_TryAlloc(data->size, 1); - } - - if (!data) return false; - ctx->_tmpData = audio; - ctx->_tmpSize = data->size; - } - - audio = ctx->_tmpData; - Mem_Copy(audio, data->data, data->size); - ApplyVolume((cc_int16*)audio, data->size / 2, data->volume); - data->data = audio; - return true; -} - -static void AudioBase_AllocChunks(int size, void** chunks, int numChunks) { - cc_uint8* dst = (cc_uint8*)Mem_TryAlloc(numChunks, size); - int i; - - for (i = 0; i < numChunks; i++) - { - chunks[i] = dst ? (dst + size * i) : NULL; - } -} - -static void AudioBase_FreeChunks(void** chunks, int numChunks) { - Mem_Free(chunks[0]); -} -#endif - /*########################################################################################################################* *--------------------------------------------------------Sounds-----------------------------------------------------------* @@ -1205,7 +46,6 @@ void Audio_PlayDigSound(cc_uint8 type) { } void Audio_PlayStepSound(cc_uint8 type) { } #else #define AUDIO_MAX_SOUNDS 10 -#define SOUND_MAX_CONTEXTS 8 struct SoundGroup { int count; @@ -1214,7 +54,6 @@ struct SoundGroup { struct Soundboard { struct SoundGroup groups[SOUND_COUNT]; }; static struct Soundboard digBoard, stepBoard; -static struct AudioContext sound_contexts[SOUND_MAX_CONTEXTS]; static RNGState sounds_rnd; #define WAV_FourCC(a, b, c, d) (((cc_uint32)a << 24) | ((cc_uint32)b << 16) | ((cc_uint32)c << 8) | (cc_uint32)d) @@ -1253,11 +92,15 @@ static cc_result Sound_ReadWaveData(struct Stream* stream, struct Sound* snd) { if (bitsPerSample != 16) return WAV_ERR_SAMPLE_BITS; size -= WAV_FMT_SIZE; } else if (fourCC == WAV_FourCC('d','a','t','a')) { - Audio_AllocChunks(size, &snd->data, 1); - snd->size = size; + if ((res = Audio_AllocChunks(size, &snd->data, 1))) return res; - if (!snd->data) return ERR_OUT_OF_MEMORY; - return Stream_Read(stream, (cc_uint8*)snd->data, size); + snd->size = size; + res = Stream_Read(stream, (cc_uint8*)snd->data, size); + + #ifdef CC_BUILD_BIGENDIAN + Utils_SwapEndian16((cc_int16*)snd->data, size / 2); + #endif + return res; } /* Skip over unhandled data */ @@ -1265,30 +108,18 @@ static cc_result Sound_ReadWaveData(struct Stream* stream, struct Sound* snd) { } } -static cc_result Sound_ReadWave(const cc_string* path, struct Sound* snd) { - struct Stream stream; - cc_result res; - - res = Stream_OpenFile(&stream, path); - if (res) return res; - res = Sound_ReadWaveData(&stream, snd); - - /* No point logging error for closing readonly file */ - (void)stream.Close(&stream); - return res; -} - -static struct SoundGroup* Soundboard_Find(struct Soundboard* board, const cc_string* name) { +static struct SoundGroup* Soundboard_FindGroup(struct Soundboard* board, const cc_string* name) { struct SoundGroup* groups = board->groups; int i; - for (i = 0; i < SOUND_COUNT; i++) { + for (i = 0; i < SOUND_COUNT; i++) + { if (String_CaselessEqualsConst(name, Sound_Names[i])) return &groups[i]; } return NULL; } -static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName, const cc_string* file) { +static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName, const cc_string* file, struct Stream* stream) { struct SoundGroup* group; struct Sound* snd; cc_string name = *file; @@ -1305,7 +136,7 @@ static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName name = String_UNSAFE_SubstringAt(&name, boardName->length); name = String_UNSAFE_Substring(&name, 0, name.length - 1); - group = Soundboard_Find(board, &name); + group = Soundboard_FindGroup(board, &name); if (!group) { Chat_Add1("&cUnknown sound group '%s'", &name); return; } @@ -1314,7 +145,7 @@ static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName } snd = &group->sounds[group->count]; - res = Sound_ReadWave(file, snd); + res = Sound_ReadWaveData(stream, snd); if (res) { Logger_SysWarn2(res, "decoding", file); @@ -1340,16 +171,14 @@ static const struct Sound* Soundboard_PickRandom(struct Soundboard* board, cc_ui CC_NOINLINE static void Sounds_Fail(cc_result res) { - AudioWarn(res, "playing sounds"); + Audio_Warn(res, "playing sounds"); Chat_AddRaw("&cDisabling sounds"); Audio_SetSounds(0); } static void Sounds_Play(cc_uint8 type, struct Soundboard* board) { - struct AudioData data; const struct Sound* snd; - struct AudioContext* ctx; - int inUse, i; + struct AudioData data; cc_result res; if (type == SOUND_NONE || !Audio_SoundsVolume) return; @@ -1372,33 +201,9 @@ static void Sounds_Play(cc_uint8 type, struct Soundboard* board) { data.volume /= 2; if (type == SOUND_METAL) data.rate = 140; } - - /* Try to play on a context that doesn't need to be recreated */ - for (i = 0; i < SOUND_MAX_CONTEXTS; i++) { - ctx = &sound_contexts[i]; - res = Audio_Poll(ctx, &inUse); - - if (res) { Sounds_Fail(res); return; } - if (inUse > 0) continue; - if (!Audio_FastPlay(ctx, &data)) continue; - - res = Audio_PlayData(ctx, &data); - if (res) Sounds_Fail(res); - return; - } - - /* Try again with all contexts, even if need to recreate one (expensive) */ - for (i = 0; i < SOUND_MAX_CONTEXTS; i++) { - ctx = &sound_contexts[i]; - res = Audio_Poll(ctx, &inUse); - - if (res) { Sounds_Fail(res); return; } - if (inUse > 0) continue; - - res = Audio_PlayData(ctx, &data); - if (res) Sounds_Fail(res); - return; - } + + res = AudioPool_Play(&data); + if (res) Sounds_Fail(res); } static void Audio_PlayBlockSound(void* obj, IVec3 coords, BlockID old, BlockID now) { @@ -1411,11 +216,29 @@ static void Audio_PlayBlockSound(void* obj, IVec3 coords, BlockID old, BlockID n } } -static void Sounds_LoadFile(const cc_string* path, void* obj) { +static cc_bool SelectZipEntry(const cc_string* path) { return true; } +static cc_result ProcessZipEntry(const cc_string* path, struct Stream* stream, struct ZipEntry* source) { static const cc_string dig = String_FromConst("dig_"); static const cc_string step = String_FromConst("step_"); - Soundboard_Load(&digBoard, &dig, path); - Soundboard_Load(&stepBoard, &step, path); + + Soundboard_Load(&digBoard, &dig, path, stream); + Soundboard_Load(&stepBoard, &step, path, stream); + return 0; +} + +static cc_result Sounds_ExtractZip(const cc_string* path) { + struct Stream stream; + cc_result res; + + res = Stream_OpenFile(&stream, path); + if (res) { Logger_SysWarn2(res, "opening", path); return res; } + + res = Zip_Extract(&stream, SelectZipEntry, ProcessZipEntry); + if (res) Logger_SysWarn2(res, "extracting", path); + + /* No point logging error for closing readonly file */ + (void)stream.Close(&stream); + return res; } /* TODO this is a pretty terrible solution */ @@ -1460,32 +283,25 @@ static void InitWebSounds(void) { static cc_bool sounds_loaded; static void Sounds_Start(void) { - int i; + cc_result res; if (!AudioBackend_Init()) { AudioBackend_Free(); Audio_SoundsVolume = 0; return; } - for (i = 0; i < SOUND_MAX_CONTEXTS; i++) { - Audio_Init(&sound_contexts[i], 1); - } - if (sounds_loaded) return; sounds_loaded = true; #ifdef CC_BUILD_WEBAUDIO InitWebSounds(); #else - Directory_Enum(&audio_dir, NULL, Sounds_LoadFile); + res = Sounds_ExtractZip(&Sounds_ZipPathMC); + if (res == ReturnCode_FileNotFound) + Sounds_ExtractZip(&Sounds_ZipPathCC); #endif } -static void Sounds_Stop(void) { - int i; - for (i = 0; i < SOUND_MAX_CONTEXTS; i++) { - Audio_Close(&sound_contexts[i]); - } -} +static void Sounds_Stop(void) { AudioPool_Close(); } static void Sounds_Init(void) { int volume = Options_GetInt(OPT_SOUND_VOLUME, 0, 100, DEFAULT_SOUNDS_VOLUME); @@ -1512,7 +328,6 @@ static void Music_Start(void) { Audio_MusicVolume = 0; } #else -static struct AudioContext music_ctx; static void* music_thread; static void* music_waitable; static volatile cc_bool music_stopping, music_joining; @@ -1529,7 +344,6 @@ static cc_result Music_Buffer(cc_int16* data, int maxSamples, struct VorbisState cur = &data[samples]; samples += Vorbis_OutputFrame(ctx, cur); } - if (Audio_MusicVolume < 100) { ApplyVolume(data, samples, Audio_MusicVolume); } res2 = Audio_QueueChunk(&music_ctx, data, samples * 2); if (res2) { music_stopping = true; return res2; } @@ -1538,8 +352,8 @@ static cc_result Music_Buffer(cc_int16* data, int maxSamples, struct VorbisState static cc_result Music_PlayOgg(struct Stream* source) { struct OggState ogg; - struct VorbisState vorbis = { 0 }; - int channels, sampleRate; + struct VorbisState vorbis; + int channels, sampleRate, volume; int chunkSize, samplesPerSecond; void* chunks[AUDIO_MAX_BUFFERS] = { 0 }; @@ -1547,26 +361,22 @@ static cc_result Music_PlayOgg(struct Stream* source) { cc_result res; Ogg_Init(&ogg, source); + Vorbis_Init(&vorbis); vorbis.source = &ogg; if ((res = Vorbis_DecodeHeaders(&vorbis))) goto cleanup; channels = vorbis.channels; sampleRate = vorbis.sampleRate; - if ((res = Audio_SetFormat(&music_ctx, channels, sampleRate))) goto cleanup; + if ((res = Audio_SetFormat(&music_ctx, channels, sampleRate, 100))) goto cleanup; /* largest possible vorbis frame decodes to blocksize1 * channels samples, */ /* so can end up decoding slightly over a second of audio */ chunkSize = channels * (sampleRate + vorbis.blockSizes[1]); samplesPerSecond = channels * sampleRate; - Audio_AllocChunks(chunkSize * 2, chunks, AUDIO_MAX_BUFFERS); - for (i = 0; i < AUDIO_MAX_BUFFERS; i++) - { - if (chunks[i]) continue; - - res = ERR_OUT_OF_MEMORY; goto cleanup; - } - + if ((res = Audio_AllocChunks(chunkSize * 2, chunks, AUDIO_MAX_BUFFERS))) goto cleanup; + volume = Audio_MusicVolume; + Audio_SetVolume(&music_ctx, volume); /* fill up with some samples before playing */ for (i = 0; i < AUDIO_MAX_BUFFERS && !res; i++) @@ -1591,6 +401,10 @@ static cc_result Music_PlayOgg(struct Stream* source) { Audio_Play(&music_ctx); } #endif + if (volume != Audio_MusicVolume) { + volume = Audio_MusicVolume; + Audio_SetVolume(&music_ctx, volume); + } res = Audio_Poll(&music_ctx, &inUse); if (res) { music_stopping = true; break; } @@ -1646,7 +460,8 @@ static void Music_RunLoop(void) { Directory_Enum(&audio_dir, &files, Music_AddFile); Random_SeedFromCurrentTime(&rnd); - Audio_Init(&music_ctx, AUDIO_MAX_BUFFERS); + res = Audio_Init(&music_ctx, AUDIO_MAX_BUFFERS); + if (res) music_stopping = true; while (!music_stopping && files.count) { idx = Random_Next(&rnd, files.count); @@ -1690,8 +505,7 @@ static void Music_Start(void) { music_joining = false; music_stopping = false; - music_thread = Thread_Create(Music_RunLoop); - Thread_Start2(music_thread, Music_RunLoop); + Thread_Run(&music_thread, Music_RunLoop, 256 * 1024, "Music"); } static void Music_Stop(void) { @@ -1750,4 +564,4 @@ static void OnFree(void) { struct IGameComponent Audio_Component = { OnInit, /* Init */ OnFree /* Free */ -}; \ No newline at end of file +}; diff --git a/src/Audio.h b/src/Audio.h index ec5d8c3ac..e054d1c2d 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -34,6 +34,8 @@ extern int Audio_SoundsVolume; /* Volume music is played at, from 0-100. */ /* NOTE: Use Audio_SetMusic, don't change this directly. */ extern int Audio_MusicVolume; +extern const cc_string Sounds_ZipPathMC; +extern const cc_string Sounds_ZipPathCC; void Audio_SetMusic(int volume); void Audio_SetSounds(int volume); @@ -41,13 +43,19 @@ void Audio_PlayDigSound(cc_uint8 type); void Audio_PlayStepSound(cc_uint8 type); #define AUDIO_MAX_BUFFERS 4 +cc_bool AudioBackend_Init(void); +void AudioBackend_Tick(void); +void AudioBackend_Free(void); + /* Initialises an audio context. */ -void Audio_Init(struct AudioContext* ctx, int buffers); +cc_result Audio_Init(struct AudioContext* ctx, int buffers); /* Stops any playing audio and then frees the audio context. */ void Audio_Close(struct AudioContext* ctx); /* Sets the format of the audio data to be played. */ /* NOTE: Changing the format can be expensive, depending on the backend. */ -cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate); +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate); +/* Sets the volume audio data is played at */ +void Audio_SetVolume(struct AudioContext* ctx, int volume); /* Queues the given audio chunk for playing. */ /* NOTE: You MUST ensure Audio_Poll indicates a buffer is free before calling this. */ /* NOTE: Some backends directly read from the data - therefore you MUST NOT modify it */ @@ -58,16 +66,18 @@ cc_result Audio_Play(struct AudioContext* ctx); /* Returns the number of buffers being played or queued */ /* (e.g. if inUse is 0, no audio buffers are being played or queued) */ cc_result Audio_Poll(struct AudioContext* ctx, int* inUse); +cc_result Audio_Pause(struct AudioContext* ctx); /* Only implemented with OpenSL ES backend */ -/* Plays the given audio data */ -cc_result Audio_PlayData(struct AudioContext* ctx, struct AudioData* data); -/* Whether the given audio data can be played without recreating the underlying audio device */ -cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data); /* Outputs more detailed information about errors with audio. */ cc_bool Audio_DescribeError(cc_result res, cc_string* dst); - /* Allocates a group of chunks of data to store audio samples */ -void Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks); +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks); /* Frees a previously allocated group of chunks of data */ void Audio_FreeChunks(void** chunks, int numChunks); + +extern struct AudioContext music_ctx; +void Audio_Warn(cc_result res, const char* action); + +cc_result AudioPool_Play(struct AudioData* data); +void AudioPool_Close(void); #endif diff --git a/src/AudioBackend.c b/src/AudioBackend.c new file mode 100644 index 000000000..fafe33c75 --- /dev/null +++ b/src/AudioBackend.c @@ -0,0 +1,1723 @@ +#include "Audio.h" +#include "String.h" +#include "Logger.h" +#include "Funcs.h" +#include "Errors.h" +#include "Utils.h" +#include "Platform.h" + +void Audio_Warn(cc_result res, const char* action) { + Logger_Warn(res, action, Audio_DescribeError); +} + +/* Whether the given audio data can be played without recreating the underlying audio device */ +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data); + +/* Common/Base methods */ +static void AudioBase_Clear(struct AudioContext* ctx); +static cc_bool AudioBase_AdjustSound(struct AudioContext* ctx, int i, void** data, cc_uint32* size); +static cc_result AudioBase_AllocChunks(int size, void** chunks, int numChunks); +static void AudioBase_FreeChunks(void** chunks, int numChunks); + +/* achieve higher speed by playing samples at higher sample rate */ +#define Audio_AdjustSampleRate(sampleRate, playbackRate) ((sampleRate * playbackRate) / 100) + +#if defined CC_BUILD_OPENAL +/*########################################################################################################################* +*------------------------------------------------------OpenAL backend-----------------------------------------------------* +*#########################################################################################################################*/ +/* Simpler to just include subset of OpenAL actually use here instead of including */ +/* === BEGIN OPENAL HEADERS === */ +#if defined _WIN32 +#define APIENTRY __cdecl +#else +#define APIENTRY +#endif +#define AL_NONE 0 +#define AL_GAIN 0x100A +#define AL_SOURCE_STATE 0x1010 +#define AL_PLAYING 0x1012 +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO16 0x1103 + +#define AL_INVALID_NAME 0xA001 +#define AL_INVALID_ENUM 0xA002 +#define AL_INVALID_VALUE 0xA003 +#define AL_INVALID_OPERATION 0xA004 +#define AL_OUT_OF_MEMORY 0xA005 + +typedef char ALboolean; +typedef int ALint; +typedef unsigned int ALuint; +typedef int ALsizei; +typedef int ALenum; + +/* Apologies for the ugly dynamic symbol definitions here */ +static ALenum (APIENTRY *_alGetError)(void); +static void (APIENTRY *_alGenSources)(ALsizei n, ALuint* sources); +static void (APIENTRY *_alDeleteSources)(ALsizei n, const ALuint* sources); +static void (APIENTRY *_alGetSourcei)(ALuint source, ALenum param, ALint* value); +static void (APIENTRY *_alSourcef)(ALuint source, ALenum param, float value); +static void (APIENTRY *_alSourcePlay)(ALuint source); +static void (APIENTRY *_alSourceStop)(ALuint source); +static void (APIENTRY *_alSourceQueueBuffers)(ALuint source, ALsizei nb, const ALuint* buffers); +static void (APIENTRY *_alSourceUnqueueBuffers)(ALuint source, ALsizei nb, ALuint* buffers); +static void (APIENTRY *_alGenBuffers)(ALsizei n, ALuint* buffers); +static void (APIENTRY *_alDeleteBuffers)(ALsizei n, const ALuint* buffers); +static void (APIENTRY *_alBufferData)(ALuint buffer, ALenum format, const void* data, ALsizei size, ALsizei freq); + +static void (APIENTRY *_alDistanceModel)(ALenum distanceModel); +static void* (APIENTRY *_alcCreateContext)(void* device, const ALint* attrlist); +static ALboolean (APIENTRY *_alcMakeContextCurrent)(void* context); +static void (APIENTRY *_alcDestroyContext)(void* context); +static void* (APIENTRY *_alcOpenDevice)(const char* devicename); +static ALboolean (APIENTRY *_alcCloseDevice)(void* device); +static ALenum (APIENTRY *_alcGetError)(void* device); +/* === END OPENAL HEADERS === */ + +struct AudioContext { + ALuint source; + ALuint buffers[AUDIO_MAX_BUFFERS]; + ALuint freeIDs[AUDIO_MAX_BUFFERS]; + int count, free, sampleRate; + ALenum format; +}; +#define AUDIO_COMMON_ALLOC + +static void* audio_device; +static void* audio_context; + +#if defined CC_BUILD_WIN +static const cc_string alLib = String_FromConst("openal32.dll"); +#elif defined CC_BUILD_MACOS +static const cc_string alLib = String_FromConst("/System/Library/Frameworks/OpenAL.framework/Versions/A/OpenAL"); +#elif defined CC_BUILD_IOS +static const cc_string alLib = String_FromConst("/System/Library/Frameworks/OpenAL.framework/OpenAL"); +#elif defined CC_BUILD_NETBSD +static const cc_string alLib = String_FromConst("/usr/pkg/lib/libopenal.so"); +#elif defined CC_BUILD_BSD +static const cc_string alLib = String_FromConst("libopenal.so"); +#else +static const cc_string alLib = String_FromConst("libopenal.so.1"); +#endif + +static cc_bool LoadALFuncs(void) { + static const struct DynamicLibSym funcs[] = { + DynamicLib_Sym(alcCreateContext), DynamicLib_Sym(alcMakeContextCurrent), + DynamicLib_Sym(alcDestroyContext), DynamicLib_Sym(alcOpenDevice), + DynamicLib_Sym(alcCloseDevice), DynamicLib_Sym(alcGetError), + + DynamicLib_Sym(alGetError), + DynamicLib_Sym(alGenSources), DynamicLib_Sym(alDeleteSources), + DynamicLib_Sym(alGetSourcei), DynamicLib_Sym(alSourcef), + DynamicLib_Sym(alSourcePlay), DynamicLib_Sym(alSourceStop), + DynamicLib_Sym(alSourceQueueBuffers), DynamicLib_Sym(alSourceUnqueueBuffers), + DynamicLib_Sym(alGenBuffers), DynamicLib_Sym(alDeleteBuffers), + DynamicLib_Sym(alBufferData), DynamicLib_Sym(alDistanceModel) + }; + void* lib; + + return DynamicLib_LoadAll(&alLib, funcs, Array_Elems(funcs), &lib); +} + +static cc_result CreateALContext(void) { + ALenum err; + audio_device = _alcOpenDevice(NULL); + if ((err = _alcGetError(audio_device))) return err; + if (!audio_device) return AL_ERR_INIT_DEVICE; + + audio_context = _alcCreateContext(audio_device, NULL); + if ((err = _alcGetError(audio_device))) return err; + if (!audio_context) return AL_ERR_INIT_CONTEXT; + + _alcMakeContextCurrent(audio_context); + return _alcGetError(audio_device); +} + +cc_bool AudioBackend_Init(void) { + static const cc_string msg = String_FromConst("Failed to init OpenAL. No audio will play."); + cc_result res; + if (audio_device) return true; + if (!LoadALFuncs()) { Logger_WarnFunc(&msg); return false; } + + res = CreateALContext(); + if (res) { Audio_Warn(res, "initing OpenAL"); return false; } + return true; +} + +void AudioBackend_Tick(void) { } + +void AudioBackend_Free(void) { + if (!audio_device) return; + _alcMakeContextCurrent(NULL); + + if (audio_context) _alcDestroyContext(audio_context); + if (audio_device) _alcCloseDevice(audio_device); + + audio_context = NULL; + audio_device = NULL; +} + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + ALenum i, err; + _alDistanceModel(AL_NONE); + ctx->source = 0; + ctx->count = buffers; + + _alGetError(); /* Reset error state */ + _alGenSources(1, &ctx->source); + if ((err = _alGetError())) return err; + + _alGenBuffers(buffers, ctx->buffers); + if ((err = _alGetError())) return err; + + for (i = 0; i < buffers; i++) { + ctx->freeIDs[i] = ctx->buffers[i]; + } + ctx->free = buffers; + return 0; +} + +static void Audio_Stop(struct AudioContext* ctx) { + _alSourceStop(ctx->source); +} + +static void Audio_Reset(struct AudioContext* ctx) { + _alDeleteSources(1, &ctx->source); + _alDeleteBuffers(ctx->count, ctx->buffers); + ctx->source = 0; +} + +static void ClearFree(struct AudioContext* ctx) { + int i; + for (i = 0; i < AUDIO_MAX_BUFFERS; i++) { + ctx->freeIDs[i] = 0; + } + ctx->free = 0; +} + +void Audio_Close(struct AudioContext* ctx) { + if (ctx->source) { + Audio_Stop(ctx); + Audio_Reset(ctx); + _alGetError(); /* Reset error state */ + } + ClearFree(ctx); + ctx->count = 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + ctx->sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate); + + if (channels == 1) { + ctx->format = AL_FORMAT_MONO16; + } else if (channels == 2) { + ctx->format = AL_FORMAT_STEREO16; + } else { + return ERR_INVALID_ARGUMENT; + } + return 0; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + _alSourcef(ctx->source, AL_GAIN, volume / 100.0f); + _alGetError(); /* Reset error state */ +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { + ALuint buffer; + ALenum err; + + if (!ctx->free) return ERR_INVALID_ARGUMENT; + buffer = ctx->freeIDs[--ctx->free]; + _alGetError(); /* Reset error state */ + + _alBufferData(buffer, ctx->format, chunk, size, ctx->sampleRate); + if ((err = _alGetError())) return err; + _alSourceQueueBuffers(ctx->source, 1, &buffer); + if ((err = _alGetError())) return err; + return 0; +} + +cc_result Audio_Play(struct AudioContext* ctx) { + _alSourcePlay(ctx->source); + return _alGetError(); +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + ALint processed = 0; + ALuint buffer; + ALenum err; + + *inUse = 0; + if (!ctx->source) return 0; + + _alGetError(); /* Reset error state */ + _alGetSourcei(ctx->source, AL_BUFFERS_PROCESSED, &processed); + if ((err = _alGetError())) return err; + + if (processed > 0) { + _alSourceUnqueueBuffers(ctx->source, 1, &buffer); + if ((err = _alGetError())) return err; + + ctx->freeIDs[ctx->free++] = buffer; + } + *inUse = ctx->count - ctx->free; return 0; +} + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + /* Channels/Sample rate is per buffer, not a per source property */ + return true; +} + +static const char* GetError(cc_result res) { + switch (res) { + case AL_ERR_INIT_CONTEXT: return "Failed to init OpenAL context"; + case AL_ERR_INIT_DEVICE: return "Failed to init OpenAL device"; + case AL_INVALID_NAME: return "Invalid parameter name"; + case AL_INVALID_ENUM: return "Invalid parameter"; + case AL_INVALID_VALUE: return "Invalid parameter value"; + case AL_INVALID_OPERATION: return "Invalid operation"; + case AL_OUT_OF_MEMORY: return "OpenAL out of memory"; + } + return NULL; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + const char* err = GetError(res); + if (err) String_AppendConst(dst, err); + return err != NULL; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + return AudioBase_AllocChunks(size, chunks, numChunks); +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + AudioBase_FreeChunks(chunks, numChunks); +} +#elif defined CC_BUILD_WINMM +/*########################################################################################################################* +*------------------------------------------------------WinMM backend------------------------------------------------------* +*#########################################################################################################################*/ +#define WIN32_LEAN_AND_MEAN +#define NOSERVICE +#define NOMCX +#define NOIME +#ifndef UNICODE +#define UNICODE +#define _UNICODE +#endif +#include + +/* === BEGIN mmsyscom.h === */ +#define CALLBACK_NULL 0x00000000l +typedef UINT MMRESULT; +#define WINMMAPI DECLSPEC_IMPORT +#define MMSYSERR_BADDEVICEID 2 +/* === BEGIN mmeapi.h === */ +typedef struct WAVEHDR_ { + LPSTR lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD_PTR dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct WAVEHDR_* lpNext; + DWORD_PTR reserved; +} WAVEHDR; + +typedef struct WAVEFORMATEX_ { + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; +} WAVEFORMATEX; +typedef void* HWAVEOUT; + +#define WAVE_MAPPER ((UINT)-1) +#define WAVE_FORMAT_PCM 1 +#define WHDR_DONE 0x00000001 +#define WHDR_PREPARED 0x00000002 + +WINMMAPI MMRESULT WINAPI waveOutOpen(HWAVEOUT* phwo, UINT deviceID, const WAVEFORMATEX* fmt, DWORD_PTR callback, DWORD_PTR instance, DWORD flags); +WINMMAPI MMRESULT WINAPI waveOutClose(HWAVEOUT hwo); +WINMMAPI MMRESULT WINAPI waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* hdr, UINT hdrSize); +WINMMAPI MMRESULT WINAPI waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* hdr, UINT hdrSize); +WINMMAPI MMRESULT WINAPI waveOutWrite(HWAVEOUT hwo, WAVEHDR* hdr, UINT hdrSize); +WINMMAPI MMRESULT WINAPI waveOutReset(HWAVEOUT hwo); +WINMMAPI MMRESULT WINAPI waveOutGetErrorTextA(MMRESULT err, LPSTR text, UINT textLen); +WINMMAPI UINT WINAPI waveOutGetNumDevs(void); +/* === END mmeapi.h === */ + +struct AudioContext { + HWAVEOUT handle; + WAVEHDR headers[AUDIO_MAX_BUFFERS]; + int count, channels, sampleRate, volume; + cc_uint32 _tmpSize[AUDIO_MAX_BUFFERS]; + void* _tmpData[AUDIO_MAX_BUFFERS]; +}; +#define AUDIO_COMMON_VOLUME +#define AUDIO_COMMON_ALLOC + +cc_bool AudioBackend_Init(void) { return true; } +void AudioBackend_Tick(void) { } +void AudioBackend_Free(void) { } + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + int i; + for (i = 0; i < buffers; i++) { + ctx->headers[i].dwFlags = WHDR_DONE; + } + ctx->count = buffers; + ctx->volume = 100; + return 0; +} + +static void Audio_Stop(struct AudioContext* ctx) { + waveOutReset(ctx->handle); +} + +static cc_result Audio_Reset(struct AudioContext* ctx) { + cc_result res; + if (!ctx->handle) return 0; + + res = waveOutClose(ctx->handle); + ctx->handle = NULL; + return res; +} + +void Audio_Close(struct AudioContext* ctx) { + int inUse; + if (ctx->handle) { + Audio_Stop(ctx); + Audio_Poll(ctx, &inUse); /* unprepare buffers */ + Audio_Reset(ctx); + } + AudioBase_Clear(ctx); +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + WAVEFORMATEX fmt; + cc_result res; + int sampleSize; + + sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate); + if (ctx->channels == channels && ctx->sampleRate == sampleRate) return 0; + ctx->channels = channels; + ctx->sampleRate = sampleRate; + + sampleSize = channels * 2; /* 16 bits per sample / 8 */ + if ((res = Audio_Reset(ctx))) return res; + + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = channels; + fmt.nSamplesPerSec = sampleRate; + fmt.nAvgBytesPerSec = sampleRate * sampleSize; + fmt.nBlockAlign = sampleSize; + fmt.wBitsPerSample = 16; + fmt.cbSize = 0; + + res = waveOutOpen(&ctx->handle, WAVE_MAPPER, &fmt, 0, 0, CALLBACK_NULL); + /* Show a better error message when no audio output devices connected than */ + /* "A device ID has been used that is out of range for your system" */ + if (res == MMSYSERR_BADDEVICEID && waveOutGetNumDevs() == 0) + return ERR_NO_AUDIO_OUTPUT; + return res; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { ctx->volume = volume; } + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { + cc_result res; + WAVEHDR* hdr; + cc_bool ok; + int i; + + for (i = 0; i < ctx->count; i++) { + hdr = &ctx->headers[i]; + if (!(hdr->dwFlags & WHDR_DONE)) continue; + + ok = AudioBase_AdjustSound(ctx, i, &chunk, &dataSize); + if (!ok) return ERR_OUT_OF_MEMORY; + + Mem_Set(hdr, 0, sizeof(WAVEHDR)); + hdr->lpData = (LPSTR)chunk; + hdr->dwBufferLength = dataSize; + hdr->dwLoops = 1; + + if ((res = waveOutPrepareHeader(ctx->handle, hdr, sizeof(WAVEHDR)))) return res; + if ((res = waveOutWrite(ctx->handle, hdr, sizeof(WAVEHDR)))) return res; + return 0; + } + /* tried to queue data without polling for free buffers first */ + return ERR_INVALID_ARGUMENT; +} + +cc_result Audio_Play(struct AudioContext* ctx) { return 0; } + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + cc_result res = 0; + WAVEHDR* hdr; + int i, count = 0; + + for (i = 0; i < ctx->count; i++) { + hdr = &ctx->headers[i]; + if (!(hdr->dwFlags & WHDR_DONE)) { count++; continue; } + + if (!(hdr->dwFlags & WHDR_PREPARED)) continue; + /* unprepare this WAVEHDR so it can be reused */ + res = waveOutUnprepareHeader(ctx->handle, hdr, sizeof(WAVEHDR)); + if (res) break; + } + + *inUse = count; return res; +} + + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + int channels = data->channels; + int sampleRate = Audio_AdjustSampleRate(data->sampleRate, data->rate); + return !ctx->channels || (ctx->channels == channels && ctx->sampleRate == sampleRate); +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + char buffer[NATIVE_STR_LEN] = { 0 }; + waveOutGetErrorTextA(res, buffer, NATIVE_STR_LEN); + + if (!buffer[0]) return false; + String_AppendConst(dst, buffer); + return true; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + return AudioBase_AllocChunks(size, chunks, numChunks); +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + AudioBase_FreeChunks(chunks, numChunks); +} +#elif defined CC_BUILD_OPENSLES +/*########################################################################################################################* +*----------------------------------------------------OpenSL ES backend----------------------------------------------------* +*#########################################################################################################################*/ +#include +#include +#include "ExtMath.h" +static SLObjectItf slEngineObject; +static SLEngineItf slEngineEngine; +static SLObjectItf slOutputObject; + +struct AudioContext { + int count, volume; + int channels, sampleRate; + SLObjectItf playerObject; + SLPlayItf playerPlayer; + SLBufferQueueItf playerQueue; + SLPlaybackRateItf playerRate; + SLVolumeItf playerVolume; +}; +#define AUDIO_COMMON_ALLOC + +static SLresult (SLAPIENTRY *_slCreateEngine)(SLObjectItf* engine, SLuint32 numOptions, const SLEngineOption* engineOptions, + SLuint32 numInterfaces, const SLInterfaceID* interfaceIds, const SLboolean* interfaceRequired); +static SLInterfaceID* _SL_IID_NULL; +static SLInterfaceID* _SL_IID_PLAY; +static SLInterfaceID* _SL_IID_ENGINE; +static SLInterfaceID* _SL_IID_BUFFERQUEUE; +static SLInterfaceID* _SL_IID_PLAYBACKRATE; +static SLInterfaceID* _SL_IID_VOLUME; +static const cc_string slLib = String_FromConst("libOpenSLES.so"); + +static cc_bool LoadSLFuncs(void) { + static const struct DynamicLibSym funcs[] = { + DynamicLib_Sym(slCreateEngine), DynamicLib_Sym(SL_IID_NULL), + DynamicLib_Sym(SL_IID_PLAY), DynamicLib_Sym(SL_IID_ENGINE), + DynamicLib_Sym(SL_IID_BUFFERQUEUE), DynamicLib_Sym(SL_IID_PLAYBACKRATE), + DynamicLib_Sym(SL_IID_VOLUME) + }; + void* lib; + + return DynamicLib_LoadAll(&slLib, funcs, Array_Elems(funcs), &lib); +} + +cc_bool AudioBackend_Init(void) { + static const cc_string msg = String_FromConst("Failed to init OpenSLES. No audio will play."); + SLInterfaceID ids[1]; + SLboolean req[1]; + SLresult res; + + if (slEngineObject) return true; + if (!LoadSLFuncs()) { Logger_WarnFunc(&msg); return false; } + + /* mixer doesn't use any effects */ + ids[0] = *_SL_IID_NULL; req[0] = SL_BOOLEAN_FALSE; + + res = _slCreateEngine(&slEngineObject, 0, NULL, 0, NULL, NULL); + if (res) { Audio_Warn(res, "creating OpenSL ES engine"); return false; } + + res = (*slEngineObject)->Realize(slEngineObject, SL_BOOLEAN_FALSE); + if (res) { Audio_Warn(res, "realising OpenSL ES engine"); return false; } + + res = (*slEngineObject)->GetInterface(slEngineObject, *_SL_IID_ENGINE, &slEngineEngine); + if (res) { Audio_Warn(res, "initing OpenSL ES engine"); return false; } + + res = (*slEngineEngine)->CreateOutputMix(slEngineEngine, &slOutputObject, 1, ids, req); + if (res) { Audio_Warn(res, "creating OpenSL ES mixer"); return false; } + + res = (*slOutputObject)->Realize(slOutputObject, SL_BOOLEAN_FALSE); + if (res) { Audio_Warn(res, "realising OpenSL ES mixer"); return false; } + + return true; +} + +void AudioBackend_Tick(void) { } + +void AudioBackend_Free(void) { + if (slOutputObject) { + (*slOutputObject)->Destroy(slOutputObject); + slOutputObject = NULL; + } + if (slEngineObject) { + (*slEngineObject)->Destroy(slEngineObject); + slEngineObject = NULL; + slEngineEngine = NULL; + } +} + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + ctx->count = buffers; + ctx->volume = 100; + return 0; +} + +static void Audio_Stop(struct AudioContext* ctx) { + if (!ctx->playerPlayer) return; + + (*ctx->playerQueue)->Clear(ctx->playerQueue); + (*ctx->playerPlayer)->SetPlayState(ctx->playerPlayer, SL_PLAYSTATE_STOPPED); +} + +static void Audio_Reset(struct AudioContext* ctx) { + SLObjectItf playerObject = ctx->playerObject; + if (!playerObject) return; + + (*playerObject)->Destroy(playerObject); + ctx->playerObject = NULL; + ctx->playerPlayer = NULL; + ctx->playerQueue = NULL; + ctx->playerRate = NULL; + ctx->playerVolume = NULL; +} + +void Audio_Close(struct AudioContext* ctx) { + Audio_Stop(ctx); + Audio_Reset(ctx); + + ctx->count = 0; + ctx->channels = 0; + ctx->sampleRate = 0; +} + +static float Log10(float volume) { return Math_Log2(volume) / Math_Log2(10); } + +static void UpdateVolume(struct AudioContext* ctx) { + /* Object doesn't exist until Audio_SetFormat is called */ + if (!ctx->playerVolume) return; + + /* log of 0 is undefined */ + SLmillibel attenuation = ctx->volume == 0 ? SL_MILLIBEL_MIN : (2000 * Log10(ctx->volume / 100.0f)); + (*ctx->playerVolume)->SetVolumeLevel(ctx->playerVolume, attenuation); +} + +static cc_result RecreatePlayer(struct AudioContext* ctx, int channels, int sampleRate) { + SLDataLocator_AndroidSimpleBufferQueue input; + SLDataLocator_OutputMix output; + SLObjectItf playerObject; + SLDataFormat_PCM fmt; + SLInterfaceID ids[4]; + SLboolean req[4]; + SLDataSource src; + SLDataSink dst; + cc_result res; + + ctx->channels = channels; + ctx->sampleRate = sampleRate; + Audio_Reset(ctx); + + fmt.formatType = SL_DATAFORMAT_PCM; + fmt.numChannels = channels; + fmt.samplesPerSec = sampleRate * 1000; + fmt.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + fmt.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; + fmt.channelMask = 0; + fmt.endianness = SL_BYTEORDER_LITTLEENDIAN; + + input.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + input.numBuffers = ctx->count; + output.locatorType = SL_DATALOCATOR_OUTPUTMIX; + output.outputMix = slOutputObject; + + src.pLocator = &input; + src.pFormat = &fmt; + dst.pLocator = &output; + dst.pFormat = NULL; + + ids[0] = *_SL_IID_BUFFERQUEUE; req[0] = SL_BOOLEAN_TRUE; + ids[1] = *_SL_IID_PLAY; req[1] = SL_BOOLEAN_TRUE; + ids[2] = *_SL_IID_PLAYBACKRATE; req[2] = SL_BOOLEAN_TRUE; + ids[3] = *_SL_IID_VOLUME; req[3] = SL_BOOLEAN_TRUE; + + res = (*slEngineEngine)->CreateAudioPlayer(slEngineEngine, &playerObject, &src, &dst, 4, ids, req); + ctx->playerObject = playerObject; + if (res) return res; + + if ((res = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE))) return res; + if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_PLAY, &ctx->playerPlayer))) return res; + if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_BUFFERQUEUE, &ctx->playerQueue))) return res; + if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_PLAYBACKRATE, &ctx->playerRate))) return res; + if ((res = (*playerObject)->GetInterface(playerObject, *_SL_IID_VOLUME, &ctx->playerVolume))) return res; + + UpdateVolume(ctx); + return 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + cc_result res; + + if (ctx->channels != channels || ctx->sampleRate != sampleRate) { + if ((res = RecreatePlayer(ctx, channels, sampleRate))) return res; + } + + /* rate is in milli, so 1000 = normal rate */ + return (*ctx->playerRate)->SetRate(ctx->playerRate, playbackRate * 10); +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + ctx->volume = volume; + UpdateVolume(ctx); +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { + return (*ctx->playerQueue)->Enqueue(ctx->playerQueue, chunk, size); +} + +cc_result Audio_Pause(struct AudioContext* ctx) { + return (*ctx->playerPlayer)->SetPlayState(ctx->playerPlayer, SL_PLAYSTATE_PAUSED); +} + +cc_result Audio_Play(struct AudioContext* ctx) { + return (*ctx->playerPlayer)->SetPlayState(ctx->playerPlayer, SL_PLAYSTATE_PLAYING); +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + SLBufferQueueState state = { 0 }; + cc_result res = 0; + + if (ctx->playerQueue) { + res = (*ctx->playerQueue)->GetState(ctx->playerQueue, &state); + } + *inUse = state.count; + return res; +} + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + return !ctx->channels || (ctx->channels == data->channels && ctx->sampleRate == data->sampleRate); +} + +static const char* GetError(cc_result res) { + switch (res) { + case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated"; + case SL_RESULT_PARAMETER_INVALID: return "Invalid parameter"; + case SL_RESULT_MEMORY_FAILURE: return "Memory failure"; + case SL_RESULT_RESOURCE_ERROR: return "Resource error"; + case SL_RESULT_RESOURCE_LOST: return "Resource lost"; + case SL_RESULT_IO_ERROR: return "I/O error"; + case SL_RESULT_BUFFER_INSUFFICIENT: return "Insufficient buffer"; + case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted"; + case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported"; + case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found"; + case SL_RESULT_PERMISSION_DENIED: return "Permission denied"; + case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported"; + case SL_RESULT_INTERNAL_ERROR: return "Internal error"; + case SL_RESULT_UNKNOWN_ERROR: return "Unknown error"; + case SL_RESULT_OPERATION_ABORTED: return "Operation aborted"; + case SL_RESULT_CONTROL_LOST: return "Control lost"; + } + return NULL; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + const char* err = GetError(res); + if (err) String_AppendConst(dst, err); + return err != NULL; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + return AudioBase_AllocChunks(size, chunks, numChunks); +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + AudioBase_FreeChunks(chunks, numChunks); +} +#elif defined CC_BUILD_3DS +/*########################################################################################################################* +*-------------------------------------------------------3DS backend-------------------------------------------------------* +*#########################################################################################################################*/ +#include <3ds.h> +struct AudioContext { + int chanID, count; + ndspWaveBuf bufs[AUDIO_MAX_BUFFERS]; + int sampleRate; + cc_bool stereo; +}; +static int channelIDs; + +// See https://github.com/devkitPro/3ds-examples/blob/master/audio/README.md +// To get audio to work in Citra, just create a 0 byte file in sdmc/3ds named dspfirm.cdca +cc_bool AudioBackend_Init(void) { + int result = ndspInit(); + Platform_Log2("NDSP_INIT: %i, %h", &result, &result); + + if (result == MAKERESULT(RL_PERMANENT, RS_NOTFOUND, RM_DSP, RD_NOT_FOUND)) { + static const cc_string msg = String_FromConst("/3ds/dspfirm.cdc not found on SD card, therefore no audio will play"); + Logger_WarnFunc(&msg); + } else if (result) { + Audio_Warn(result, "initing DSP for playing audio"); + } + + ndspSetOutputMode(NDSP_OUTPUT_STEREO); + return result == 0; +} + +void AudioBackend_Tick(void) { } + +void AudioBackend_Free(void) { } + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + int chanID = -1; + + for (int i = 0; i < 24; i++) + { + // channel in use + if (channelIDs & (1 << i)) continue; + + chanID = i; break; + } + if (chanID == -1) return ERR_INVALID_ARGUMENT; + + channelIDs |= (1 << chanID); + ctx->count = buffers; + ctx->chanID = chanID; + + ndspChnSetInterp(ctx->chanID, NDSP_INTERP_LINEAR); + return 0; +} + +void Audio_Close(struct AudioContext* ctx) { + if (ctx->count) { + ndspChnWaveBufClear(ctx->chanID); + channelIDs &= ~(1 << ctx->chanID); + } + ctx->count = 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + ctx->stereo = (channels == 2); + int fmt = ctx->stereo ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16; + + sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate); + ndspChnSetFormat(ctx->chanID, fmt); + ndspChnSetRate(ctx->chanID, sampleRate); + return 0; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + float mix[12] = { 0 }; + mix[0] = volume / 100.0f; + mix[1] = volume / 100.0f; + + ndspChnSetMix(ctx->chanID, mix); +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { + ndspWaveBuf* buf; + + // DSP audio buffers must be aligned to a multiple of 0x80, according to the example code I could find. + if (((uintptr_t)chunk & 0x7F) != 0) { + Platform_Log1("Audio_QueueData: tried to queue buffer with non-aligned audio buffer 0x%x\n", &chunk); + } + if ((dataSize & 0x7F) != 0) { + Platform_Log1("Audio_QueueData: unaligned audio data size 0x%x\n", &dataSize); + } + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (buf->status == NDSP_WBUF_QUEUED || buf->status == NDSP_WBUF_PLAYING) + continue; + + buf->data_pcm16 = chunk; + buf->nsamples = dataSize / (sizeof(cc_int16) * (ctx->stereo ? 2 : 1)); + DSP_FlushDataCache(buf->data_pcm16, dataSize); + ndspChnWaveBufAdd(ctx->chanID, buf); + return 0; + } + // tried to queue data without polling for free buffers first + return ERR_INVALID_ARGUMENT; +} + +cc_result Audio_Play(struct AudioContext* ctx) { return 0; } + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + ndspWaveBuf* buf; + int count = 0; + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (buf->status == NDSP_WBUF_QUEUED || buf->status == NDSP_WBUF_PLAYING) { + count++; continue; + } + } + + *inUse = count; + return 0; +} + + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + return true; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + size = (size + 0x7F) & ~0x7F; // round up to nearest multiple of 0x80 + cc_uint8* dst = linearAlloc(size * numChunks); + if (!dst) return ERR_OUT_OF_MEMORY; + + for (int i = 0; i < numChunks; i++) { + chunks[i] = dst + size * i; + } + return 0; +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + linearFree(chunks[0]); +} +#elif defined CC_BUILD_SWITCH +/*########################################################################################################################* +*-----------------------------------------------------Switch backend------------------------------------------------------* +*#########################################################################################################################*/ +#include +#include +#include + +struct AudioContext { + int chanID, count; + AudioDriverWaveBuf bufs[AUDIO_MAX_BUFFERS]; + int channels, sampleRate; +}; +struct AudioMemPools { + void* chunk; + int mpid; +}; + +static int channelIDs; +static struct AudioMemPools audioPools[64]; +AudioDriver drv; +bool switchAudio = false; +void* audrv_mutex; + +cc_bool AudioBackend_Init(void) { + if (switchAudio) return true; + switchAudio = true; + + if (!audrv_mutex) audrv_mutex = Mutex_Create(); + + Mem_Set(audioPools, 0, sizeof(audioPools)); + + static const AudioRendererConfig arConfig = + { + .output_rate = AudioRendererOutputRate_48kHz, + .num_voices = 24, + .num_effects = 0, + .num_sinks = 1, + .num_mix_objs = 1, + .num_mix_buffers = 2, + }; + + audrenInitialize(&arConfig); + audrvCreate(&drv, &arConfig, 2); + + static const u8 sink_channels[] = { 0, 1 }; + /*int sink =*/ audrvDeviceSinkAdd(&drv, AUDREN_DEFAULT_DEVICE_NAME, 2, sink_channels); + + audrvUpdate(&drv); + + Result res = audrenStartAudioRenderer(); + + return R_SUCCEEDED(res); +} + +void AudioBackend_Tick(void) { + Mutex_Lock(audrv_mutex); + if (switchAudio) audrvUpdate(&drv); + Mutex_Unlock(audrv_mutex); +} + +void AudioBackend_Free(void) { + for (int i = 0; i < 24; i++) { + audrvVoiceStop(&drv, i); + } + audrvUpdate(&drv); +} + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + int chanID = -1; + + for (int i = 0; i < 24; i++) + { + // channel in use + if (channelIDs & (1 << i)) continue; + + chanID = i; break; + } + if (chanID == -1) return ERR_INVALID_ARGUMENT; + + channelIDs |= (1 << chanID); + ctx->count = buffers; + ctx->chanID = chanID; + return 0; +} + +void Audio_Close(struct AudioContext* ctx) { + if (ctx->count) { + audrvVoiceStop(&drv, ctx->chanID); + channelIDs &= ~(1 << ctx->chanID); + } + ctx->count = 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate); + ctx->channels = channels; + ctx->sampleRate = sampleRate; + + audrvVoiceStop(&drv, ctx->chanID); + audrvVoiceInit(&drv, ctx->chanID, ctx->channels, PcmFormat_Int16, ctx->sampleRate); + audrvVoiceSetDestinationMix(&drv, ctx->chanID, AUDREN_FINAL_MIX_ID); + + if (channels == 1) { + // mono + audrvVoiceSetMixFactor(&drv, ctx->chanID, 1.0f, 0, 0); + audrvVoiceSetMixFactor(&drv, ctx->chanID, 1.0f, 0, 1); + } else { + // stereo + audrvVoiceSetMixFactor(&drv, ctx->chanID, 1.0f, 0, 0); + audrvVoiceSetMixFactor(&drv, ctx->chanID, 0.0f, 0, 1); + audrvVoiceSetMixFactor(&drv, ctx->chanID, 0.0f, 1, 0); + audrvVoiceSetMixFactor(&drv, ctx->chanID, 1.0f, 1, 1); + } + + return 0; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + audrvVoiceSetVolume(&drv, ctx->chanID, volume / 100.0f); +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { + AudioDriverWaveBuf* buf; + + // Audio buffers must be aligned to a multiple of 0x1000, according to libnx example code + if (((uintptr_t)chunk & 0xFFF) != 0) { + Platform_Log1("Audio_QueueData: tried to queue buffer with non-aligned audio buffer 0x%x\n", &chunk); + } + if ((dataSize & 0xFFF) != 0) { + Platform_Log1("Audio_QueueData: unaligned audio data size 0x%x\n", &dataSize); + } + + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + int state = buf->state; + cc_uint32 size = dataSize; + cc_uint32 endOffset = dataSize / (sizeof(cc_int16) * ((ctx->channels == 2) ? 2 : 1)); + + if (state == AudioDriverWaveBufState_Queued || state == AudioDriverWaveBufState_Playing || state == AudioDriverWaveBufState_Waiting) + continue; + + buf->data_pcm16 = chunk; + buf->size = size; + buf->start_sample_offset = 0; + buf->end_sample_offset = endOffset; + + Mutex_Lock(audrv_mutex); + audrvVoiceAddWaveBuf(&drv, ctx->chanID, buf); + Mutex_Unlock(audrv_mutex); + + return 0; + } + + // tried to queue data without polling for free buffers first + return ERR_INVALID_ARGUMENT; +} + +cc_result Audio_Play(struct AudioContext* ctx) { + audrvVoiceStart(&drv, ctx->chanID); + return 0; +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + AudioDriverWaveBuf* buf; + int count = 0; + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (buf->state == AudioDriverWaveBufState_Queued || buf->state == AudioDriverWaveBufState_Playing || buf->state == AudioDriverWaveBufState_Waiting) { + count++; continue; + } + } + + *inUse = count; + return 0; +} + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + return true; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + size = (size + 0xFFF) & ~0xFFF; // round up to nearest multiple of 0x1000 + void* dst = aligned_alloc(0x1000, size * numChunks); + if (!dst) return ERR_OUT_OF_MEMORY; + + for (int i = 0; i < numChunks; i++) { + chunks[i] = dst + size * i; + + int mpid = audrvMemPoolAdd(&drv, dst + size * i, size); + audrvMemPoolAttach(&drv, mpid); + + for (int j = 0; j < 64; j++) { + if (audioPools[j].chunk != NULL) continue; + audioPools[j].chunk = dst; + audioPools[j].mpid = mpid; + break; + } + } + return 0; +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + // remove memory pool from audren + for (int i=0; i +#include +#include +#include + +struct AudioBuffer { + int available; + int size; + void* samples; +}; + +struct AudioContext { + int chanID, count, bufHead; + struct AudioBuffer bufs[AUDIO_MAX_BUFFERS]; + int channels, sampleRate, volume; + cc_bool makeAvailable; +}; + +cc_bool AudioBackend_Init(void) { + ASND_Init(); + ASND_Pause(0); + return true; +} + +void AudioBackend_Tick(void) { } + +void AudioBackend_Free(void) { + ASND_Pause(1); + ASND_End(); +} + +void MusicCallback(s32 voice) { + struct AudioContext* ctx = &music_ctx; + struct AudioBuffer* nextBuf = &ctx->bufs[(ctx->bufHead + 1) % ctx->count]; + + if (ASND_StatusVoice(voice) != SND_WORKING) return; + + if (ASND_AddVoice(voice, nextBuf->samples, nextBuf->size) == SND_OK) { + ctx->bufHead = (ctx->bufHead + 1) % ctx->count; + if (ctx->bufHead == 2) ctx->makeAvailable = true; + if (ctx->makeAvailable) { + int prev = ctx->bufHead - 2; + if (prev < 0) prev += 4; + ctx->bufs[prev].available = true; + } + } + + int inUse; + Audio_Poll(ctx, &inUse); + if (!inUse) { + // music has finished, stop the voice so this function isn't called anymore + ASND_StopVoice(ctx->chanID); + } +} + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + ctx->chanID = -1; + ctx->count = buffers; + ctx->volume = 255; + ctx->bufHead = 0; + ctx->makeAvailable = false; + + Mem_Set(ctx->bufs, 0, sizeof(ctx->bufs)); + for (int i = 0; i < buffers; i++) { + ctx->bufs[i].available = true; + } + + return 0; +} + +void Audio_Close(struct AudioContext* ctx) { + if (ctx->chanID != -1) ASND_StopVoice(ctx->chanID); + ctx->chanID = -1; + ctx->count = 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate); + ctx->channels = channels; + ctx->sampleRate = sampleRate; + ctx->chanID = ASND_GetFirstUnusedVoice(); + + return 0; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + ctx->volume = (volume / 100.0f) * 255; +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { + // Audio buffers must be aligned and padded to a multiple of 32 bytes + if (((uintptr_t)chunk & 0x1F) != 0) { + Platform_Log1("Audio_QueueData: tried to queue buffer with non-aligned audio buffer 0x%x\n", &chunk); + } + + struct AudioBuffer* buf; + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (!buf->available) continue; + + buf->samples = chunk; + buf->size = dataSize; + buf->available = false; + + return 0; + } + + // tried to queue data without polling for free buffers first + return ERR_INVALID_ARGUMENT; +} + +cc_result Audio_Play(struct AudioContext* ctx) { + int format = (ctx->channels == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; + ASND_SetVoice(ctx->chanID, format, ctx->sampleRate, 0, ctx->bufs[0].samples, ctx->bufs[0].size, ctx->volume, ctx->volume, (ctx->count > 1) ? MusicCallback : NULL); + if (ctx->count == 1) ctx->bufs[0].available = true; + + return 0; +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + struct AudioBuffer* buf; + int count = 0; + + for (int i = 0; i < ctx->count; i++) { + buf = &ctx->bufs[i]; + if (!buf->available) count++; + } + + *inUse = count; + return 0; +} + + +cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + return true; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + size = (size + 0x1F) & ~0x1F; // round up to nearest multiple of 0x20 + void* dst = memalign(0x20, size * numChunks); + if (!dst) return ERR_OUT_OF_MEMORY; + + for (int i = 0; i < numChunks; i++) { + chunks[i] = dst + size * i; + } + return 0; +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + free(chunks[0]); +} +#elif defined CC_BUILD_DREAMCAST +/*########################################################################################################################* +*----------------------------------------------------Dreamcast backend----------------------------------------------------* +*#########################################################################################################################*/ +#include +/* TODO needs way more testing, especially with sounds */ +static cc_bool valid_handles[SND_STREAM_MAX]; + +struct AudioBuffer { + int available; + int bytesLeft; + void* samples; +}; + +struct AudioContext { + int bufHead, channels; + snd_stream_hnd_t hnd; + struct AudioBuffer bufs[AUDIO_MAX_BUFFERS]; + int count, sampleRate; +}; + +cc_bool AudioBackend_Init(void) { + return snd_stream_init() == 0; +} + +void AudioBackend_Tick(void) { + // TODO is this really threadsafe with music? should this be done in Audio_Poll instead? + for (int i = 0; i < SND_STREAM_MAX; i++) + { + if (valid_handles[i]) snd_stream_poll(i); + } +} + +void AudioBackend_Free(void) { + snd_stream_shutdown(); +} + +static void* AudioCallback(snd_stream_hnd_t hnd, int smp_req, int *smp_recv) { + struct AudioContext* ctx = snd_stream_get_userdata(hnd); + struct AudioBuffer* buf = &ctx->bufs[ctx->bufHead]; + + int samples = min(buf->bytesLeft, smp_req); + *smp_recv = samples; + void* ptr = buf->samples; + + buf->samples += samples; + buf->bytesLeft -= samples; + + if (buf->bytesLeft == 0) { + ctx->bufHead = (ctx->bufHead + 1) % ctx->count; + buf->samples = NULL; + buf->available = true; + + // special case to fix sounds looping + if (samples == 0 && ptr == NULL) *smp_recv = smp_req; + } + return ptr; +} + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + ctx->hnd = snd_stream_alloc(AudioCallback, SND_STREAM_BUFFER_MAX); + if (ctx->hnd == SND_STREAM_INVALID) return ERR_NOT_SUPPORTED; + snd_stream_set_userdata(ctx->hnd, ctx); + + Mem_Set(ctx->bufs, 0, sizeof(ctx->bufs)); + for (int i = 0; i < buffers; i++) { + ctx->bufs[i].available = true; + } + + ctx->count = buffers; + ctx->bufHead = 0; + valid_handles[ctx->hnd] = true; + return 0; +} + +void Audio_Close(struct AudioContext* ctx) { + if (ctx->count) { + snd_stream_stop(ctx->hnd); + snd_stream_destroy(ctx->hnd); + valid_handles[ctx->hnd] = false; + } + + ctx->hnd = SND_STREAM_INVALID; + ctx->count = 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + sampleRate = Audio_AdjustSampleRate(sampleRate, playbackRate); + ctx->channels = channels; + ctx->sampleRate = sampleRate; + return 0; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + snd_stream_volume(ctx->hnd, volume); +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { + struct AudioBuffer* buf; + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (!buf->available) continue; + + buf->samples = chunk; + buf->bytesLeft = dataSize; + buf->available = false; + return 0; + } + // tried to queue data without polling for free buffers first + return ERR_INVALID_ARGUMENT; +} + +cc_result Audio_Play(struct AudioContext* ctx) { + snd_stream_start(ctx->hnd, ctx->sampleRate, ctx->channels == 2); + return 0; +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + struct AudioBuffer* buf; + int count = 0; + + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (!buf->available) count++; + } + + *inUse = count; + return 0; +} + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + return true; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + size = (size + 0x1F) & ~0x1F; // round up to nearest multiple of 32 + void* dst = memalign(32, size * numChunks); + if (!dst) return ERR_OUT_OF_MEMORY; + + for (int i = 0; i < numChunks; i++) { + chunks[i] = dst + size * i; + } + return 0; +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + free(chunks[0]); +} + +#elif defined CC_BUILD_WEBAUDIO +/*########################################################################################################################* +*-----------------------------------------------------WebAudio backend----------------------------------------------------* +*#########################################################################################################################*/ +struct AudioContext { int contextID, count, rate; void* data; }; +#define AUDIO_COMMON_ALLOC + +extern int interop_InitAudio(void); +extern int interop_AudioCreate(void); +extern void interop_AudioClose(int contextID); +extern int interop_AudioPlay(int contextID, const void* name, int rate); +extern int interop_AudioPoll(int contextID, int* inUse); +extern int interop_AudioVolume(int contextID, int volume); +extern int interop_AudioDescribe(int res, char* buffer, int bufferLen); + +cc_bool AudioBackend_Init(void) { + cc_result res = interop_InitAudio(); + if (res) { Audio_Warn(res, "initing WebAudio context"); return false; } + return true; +} + +void AudioBackend_Tick(void) { } +void AudioBackend_Free(void) { } + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + ctx->count = buffers; + ctx->contextID = interop_AudioCreate(); + ctx->data = NULL; + ctx->rate = 100; + return 0; +} + +void Audio_Close(struct AudioContext* ctx) { + if (ctx->contextID) interop_AudioClose(ctx->contextID); + ctx->contextID = 0; + ctx->count = 0; +} + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + ctx->rate = playbackRate; return 0; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { + interop_AudioVolume(ctx->contextID, volume); +} + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { + ctx->data = chunk; return 0; +} + +cc_result Audio_Play(struct AudioContext* ctx) { + return interop_AudioPlay(ctx->contextID, ctx->data, ctx->rate); +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + return interop_AudioPoll(ctx->contextID, inUse); +} + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + /* Channels/Sample rate is per buffer, not a per source property */ + return true; +} + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { + char buffer[NATIVE_STR_LEN]; + int len = interop_AudioDescribe(res, buffer, NATIVE_STR_LEN); + + String_AppendUtf8(dst, buffer, len); + return len > 0; +} + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + return AudioBase_AllocChunks(size, chunks, numChunks); +} + +void Audio_FreeChunks(void** chunks, int numChunks) { + AudioBase_FreeChunks(chunks, numChunks); +} +#else +/*########################################################################################################################* +*----------------------------------------------------Null/Empty backend---------------------------------------------------* +*#########################################################################################################################*/ +struct AudioContext { int count; }; + +cc_bool AudioBackend_Init(void) { return false; } +void AudioBackend_Tick(void) { } +void AudioBackend_Free(void) { } + +cc_result Audio_Init(struct AudioContext* ctx, int buffers) { + return ERR_NOT_SUPPORTED; +} + +void Audio_Close(struct AudioContext* ctx) { } + +cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate, int playbackRate) { + return ERR_NOT_SUPPORTED; +} + +void Audio_SetVolume(struct AudioContext* ctx, int volume) { } + +cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 size) { + return ERR_NOT_SUPPORTED; +} + +cc_result Audio_Play(struct AudioContext* ctx) { + return ERR_NOT_SUPPORTED; +} + +cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { + return ERR_NOT_SUPPORTED; +} + +static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { return false; } + +cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { return false; } + +cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { + return ERR_NOT_SUPPORTED; +} + +void Audio_FreeChunks(void** chunks, int numChunks) { } +#endif + + +/*########################################################################################################################* +*---------------------------------------------------Common backend code---------------------------------------------------* +*#########################################################################################################################*/ + +#ifdef AUDIO_COMMON_VOLUME +static void ApplyVolume(cc_int16* samples, int count, int volume) { + int i; + + for (i = 0; i < (count & ~0x07); i += 8, samples += 8) { + samples[0] = (samples[0] * volume / 100); + samples[1] = (samples[1] * volume / 100); + samples[2] = (samples[2] * volume / 100); + samples[3] = (samples[3] * volume / 100); + + samples[4] = (samples[4] * volume / 100); + samples[5] = (samples[5] * volume / 100); + samples[6] = (samples[6] * volume / 100); + samples[7] = (samples[7] * volume / 100); + } + + for (; i < count; i++, samples++) { + samples[0] = (samples[0] * volume / 100); + } +} + +static void AudioBase_Clear(struct AudioContext* ctx) { + int i; + ctx->count = 0; + ctx->channels = 0; + ctx->sampleRate = 0; + + for (i = 0; i < AUDIO_MAX_BUFFERS; i++) + { + Mem_Free(ctx->_tmpData[i]); + ctx->_tmpData[i] = NULL; + ctx->_tmpSize[i] = 0; + } +} + +static cc_bool AudioBase_AdjustSound(struct AudioContext* ctx, int i, void** data, cc_uint32* size) { + void* audio; + cc_uint32 src_size = *size; + if (ctx->volume >= 100) return true; + + /* copy to temp buffer to apply volume */ + if (ctx->_tmpSize[i] < src_size) { + /* TODO: check if we can realloc NULL without a problem */ + if (ctx->_tmpData[i]) { + audio = Mem_TryRealloc(ctx->_tmpData[i], src_size, 1); + } else { + audio = Mem_TryAlloc(src_size, 1); + } + + if (!data) return false; + ctx->_tmpData[i] = audio; + ctx->_tmpSize[i] = src_size; + } + + audio = ctx->_tmpData[i]; + Mem_Copy(audio, *data, src_size); + ApplyVolume((cc_int16*)audio, src_size / 2, ctx->volume); + + *data = audio; + *size = src_size; + return true; +} +#endif + +#ifdef AUDIO_COMMON_ALLOC +static cc_result AudioBase_AllocChunks(int size, void** chunks, int numChunks) { + cc_uint8* dst = (cc_uint8*)Mem_TryAlloc(numChunks, size); + int i; + if (!dst) return ERR_OUT_OF_MEMORY; + + for (i = 0; i < numChunks; i++) + { + chunks[i] = dst + size * i; + } + return 0; +} + +static void AudioBase_FreeChunks(void** chunks, int numChunks) { + Mem_Free(chunks[0]); +} +#endif + + +/*########################################################################################################################* +*---------------------------------------------------Audio context code----------------------------------------------------* +*#########################################################################################################################*/ +struct AudioContext music_ctx; +#define POOL_MAX_CONTEXTS 8 +static struct AudioContext context_pool[POOL_MAX_CONTEXTS]; + +#ifndef CC_BUILD_NOSOUNDS +static cc_result PlayAudio(struct AudioContext* ctx, struct AudioData* data) { + cc_result res; + Audio_SetVolume(ctx, data->volume); + + if ((res = Audio_SetFormat(ctx, data->channels, data->sampleRate, data->rate))) return res; + if ((res = Audio_QueueChunk(ctx, data->data, data->size))) return res; + if ((res = Audio_Play(ctx))) return res; + return 0; +} + +cc_result AudioPool_Play(struct AudioData* data) { + struct AudioContext* ctx; + int inUse, i; + cc_result res; + + /* Try to play on a context that doesn't need to be recreated */ + for (i = 0; i < POOL_MAX_CONTEXTS; i++) { + ctx = &context_pool[i]; + if (!ctx->count && (res = Audio_Init(ctx, 1))) return res; + + if ((res = Audio_Poll(ctx, &inUse))) return res; + if (inUse > 0) continue; + + if (!Audio_FastPlay(ctx, data)) continue; + return PlayAudio(ctx, data); + } + + /* Try again with all contexts, even if need to recreate one (expensive) */ + for (i = 0; i < POOL_MAX_CONTEXTS; i++) { + ctx = &context_pool[i]; + res = Audio_Poll(ctx, &inUse); + + if (res) return res; + if (inUse > 0) continue; + + return PlayAudio(ctx, data); + } + return 0; +} + +void AudioPool_Close(void) { + int i; + for (i = 0; i < POOL_MAX_CONTEXTS; i++) { + Audio_Close(&context_pool[i]); + } +} +#endif diff --git a/src/AxisLinesRenderer.c b/src/AxisLinesRenderer.c index e1280025f..59d5b7670 100644 --- a/src/AxisLinesRenderer.c +++ b/src/AxisLinesRenderer.c @@ -30,21 +30,23 @@ void AxisLinesRenderer_Render(void) { Vec3 coords[5], pos, dirVector; int i, count; float axisLengthScale, axisThicknessScale; + struct Entity* e; if (!AxisLinesRenderer_Enabled) return; /* Don't do it in a ContextRecreated handler, because we only want VB recreated if ShowAxisLines in on. */ if (!axisLines_vb) { axisLines_vb = Gfx_CreateDynamicVb(VERTEX_FORMAT_COLOURED, AXISLINES_NUM_VERTICES); } + e = &Entities.CurPlayer->Base; if (Camera.Active->isThirdPerson) { - pos = LocalPlayer_Instance.Base.Position; + pos = e->Position; axisLengthScale = 1; axisThicknessScale = 1; pos.y += 0.05f; } else { pos = Camera.CurrentPos; - dirVector = Vec3_GetDirVector(LocalPlayer_Instance.Base.Yaw * MATH_DEG2RAD, LocalPlayer_Instance.Base.Pitch * MATH_DEG2RAD); + dirVector = Vec3_GetDirVector(e->Yaw * MATH_DEG2RAD, e->Pitch * MATH_DEG2RAD); Vec3_Mul1(&dirVector, &dirVector, 0.5f); Vec3_Add(&pos, &dirVector, &pos); axisLengthScale = 1.0f / 32.0f; diff --git a/src/Bitmap.c b/src/Bitmap.c index 043423fae..af8c0d911 100644 --- a/src/Bitmap.c +++ b/src/Bitmap.c @@ -6,6 +6,7 @@ #include "Stream.h" #include "Errors.h" #include "Utils.h" +#include "Funcs.h" BitmapCol BitmapColor_Offset(BitmapCol color, int rBy, int gBy, int bBy) { int r, g, b; @@ -88,12 +89,35 @@ cc_bool Png_Detect(const cc_uint8* data, cc_uint32 len) { /* 9 Filtering */ /* 13.9 Filtering */ -static void Png_Reconstruct(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint8* prior, cc_uint32 lineLen) { +static void Png_ReconstructFirst(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint32 lineLen) { + /* First scanline is a special case, where all values in prior array are 0 */ cc_uint32 i, j; + switch (type) { - case PNG_FILTER_NONE: + case PNG_FILTER_SUB: + for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { + line[i] += line[j]; + } return; + case PNG_FILTER_AVERAGE: + for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { + line[i] += (line[j] >> 1); + } + return; + + case PNG_FILTER_PAETH: + for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { + line[i] += line[j]; + } + return; + } +} + +static void Png_Reconstruct(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint8* prior, cc_uint32 lineLen) { + cc_uint32 i, j; + + switch (type) { case PNG_FILTER_SUB: for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { line[i] += line[j]; @@ -139,136 +163,103 @@ static void Png_Reconstruct(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* lin /* 7.2 Scanlines */ #define PNG_Do_Grayscale(dstI, src, scale) rgb = (src) * scale; Bitmap_Set(dst[dstI], rgb, rgb, rgb, 255); -#define PNG_Do_Grayscale_8(dstI, srcI) rgb = src[srcI]; Bitmap_Set(dst[dstI], rgb, rgb, rgb, 255); -#define PNG_Do_Grayscale_A__8(dstI, srcI) rgb = src[srcI]; Bitmap_Set(dst[dstI], rgb, rgb, rgb, src[srcI + 1]); -#define PNG_Do_RGB__8(dstI, srcI) Bitmap_Set(dst[dstI], src[srcI], src[srcI + 1], src[srcI + 2], 255); -#define PNG_Do_RGB_A__8(dstI, srcI) Bitmap_Set(dst[dstI], src[srcI], src[srcI + 1], src[srcI + 2], src[srcI + 3]); +#define PNG_Do_Grayscale_8() rgb = src[0]; Bitmap_Set(*dst, rgb, rgb, rgb, 255); dst--; src -= 1; +#define PNG_Do_Grayscale_A__8() rgb = src[0]; Bitmap_Set(*dst, rgb, rgb, rgb, src[1]); dst--; src -= 2; +#define PNG_Do_RGB__8() Bitmap_Set(*dst, src[0], src[1], src[2], 255); dst--; src -= 3; +#define PNG_Do_RGB_A__8() Bitmap_Set(*dst, src[0], src[1], src[2], src[3]); dst++; src += 4; +#define PNG_Do_Palette__8() *dst-- = palette[*src--]; -#define PNG_Mask_1(i) (7 - (i & 7)) +#define PNG_Mask_1(i) (7 - (i & 7)) #define PNG_Mask_2(i) ((3 - (i & 3)) * 2) #define PNG_Mask_4(i) ((1 - (i & 1)) * 4) #define PNG_Get__1(i) ((src[i >> 3] >> PNG_Mask_1(i)) & 1) #define PNG_Get__2(i) ((src[i >> 2] >> PNG_Mask_2(i)) & 3) +#define PNG_Get__4(i) ((src[i >> 1] >> PNG_Mask_4(i)) & 7) static void Png_Expand_GRAYSCALE_1(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { int i; cc_uint8 rgb; /* NOTE: not optimised*/ - for (i = 0; i < width; i++) { PNG_Do_Grayscale(i, PNG_Get__1(i), 255); } + for (i = width - 1; i >= 0; i--) { PNG_Do_Grayscale(i, PNG_Get__1(i), 255); } } static void Png_Expand_GRAYSCALE_2(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { int i; cc_uint8 rgb; /* NOTE: not optimised */ - for (i = 0; i < width; i++) { PNG_Do_Grayscale(i, PNG_Get__2(i), 85); } + for (i = width - 1; i >= 0; i--) { PNG_Do_Grayscale(i, PNG_Get__2(i), 85); } } static void Png_Expand_GRAYSCALE_4(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; cc_uint8 rgb; - - for (i = 0, j = 0; i < (width & ~0x1); i += 2, j++) { - PNG_Do_Grayscale(i, src[j] >> 4, 17); PNG_Do_Grayscale(i + 1, src[j] & 0x0F, 17); - } - for (; i < width; i++) { - PNG_Do_Grayscale(i, (src[j] >> PNG_Mask_4(i)) & 15, 17); - } + int i; cc_uint8 rgb; + for (i = width - 1; i >= 0; i--) { PNG_Do_Grayscale(i, PNG_Get__4(i), 17); } } static void Png_Expand_GRAYSCALE_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i; cc_uint8 rgb; + cc_uint8 rgb; + src += (width - 1) * 2; + dst += (width - 1); - for (i = 0; i < (width & ~0x3); i += 4) { - PNG_Do_Grayscale_8(i , i ); PNG_Do_Grayscale_8(i + 1, i + 1); - PNG_Do_Grayscale_8(i + 2, i + 2); PNG_Do_Grayscale_8(i + 3, i + 3); - } - for (; i < width; i++) { PNG_Do_Grayscale_8(i, i); } -} - -static void Png_Expand_GRAYSCALE_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i; cc_uint8 rgb; /* NOTE: not optimised */ - for (i = 0; i < width; i++) { - rgb = src[i * 2]; Bitmap_Set(dst[i], rgb, rgb, rgb, 255); + for (; width >= 4; width -= 4) { + PNG_Do_Grayscale_8(); PNG_Do_Grayscale_8(); + PNG_Do_Grayscale_8(); PNG_Do_Grayscale_8(); } + for (; width > 0; width--) { PNG_Do_Grayscale_8(); } } static void Png_Expand_RGB_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; + src += (width - 1) * 3; + dst += (width - 1); - for (i = 0, j = 0; i < (width & ~0x03); i += 4, j += 12) { - PNG_Do_RGB__8(i , j ); PNG_Do_RGB__8(i + 1, j + 3); - PNG_Do_RGB__8(i + 2, j + 6); PNG_Do_RGB__8(i + 3, j + 9); - } - for (; i < width; i++, j += 3) { PNG_Do_RGB__8(i, j); } -} - -static void Png_Expand_RGB_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; /* NOTE: not optimised */ - for (i = 0, j = 0; i < width; i++, j += 6) { - Bitmap_Set(dst[i], src[j], src[j + 2], src[j + 4], 255); + for (; width >= 4; width -= 4) { + PNG_Do_RGB__8(); PNG_Do_RGB__8(); + PNG_Do_RGB__8(); PNG_Do_RGB__8(); } + for (; width > 0; width--) { PNG_Do_RGB__8(); } } static void Png_Expand_INDEXED_1(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { int i; /* NOTE: not optimised */ - for (i = 0; i < width; i++) { dst[i] = palette[PNG_Get__1(i)]; } + for (i = width - 1; i >= 0; i--) { dst[i] = palette[PNG_Get__1(i)]; } } static void Png_Expand_INDEXED_2(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { int i; /* NOTE: not optimised */ - for (i = 0; i < width; i++) { dst[i] = palette[PNG_Get__2(i)]; } + for (i = width - 1; i >= 0; i--) { dst[i] = palette[PNG_Get__2(i)]; } } static void Png_Expand_INDEXED_4(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; cc_uint8 cur; - - for (i = 0, j = 0; i < (width & ~0x1); i += 2, j++) { - cur = src[j]; - dst[i] = palette[cur >> 4]; dst[i + 1] = palette[cur & 0x0F]; - } - for (; i < width; i++) { - dst[i] = palette[(src[j] >> PNG_Mask_4(i)) & 15]; - } + int i; /* NOTE: not optimised */ + for (i = width - 1; i >= 0; i--) { dst[i] = palette[PNG_Get__4(i)]; } } static void Png_Expand_INDEXED_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i; + src += (width - 1) * 1; + dst += (width - 1); - for (i = 0; i < (width & ~0x3); i += 4) { - dst[i] = palette[src[i]]; dst[i + 1] = palette[src[i + 1]]; - dst[i + 2] = palette[src[i + 2]]; dst[i + 3] = palette[src[i + 3]]; + for (; width >= 4; width -= 4) { + PNG_Do_Palette__8(); PNG_Do_Palette__8(); + PNG_Do_Palette__8(); PNG_Do_Palette__8(); } - for (; i < width; i++) { dst[i] = palette[src[i]]; } + for (; width > 0; width--) { PNG_Do_Palette__8(); } } static void Png_Expand_GRAYSCALE_A_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; cc_uint8 rgb; + cc_uint8 rgb; + src += (width - 1) * 2; + dst += (width - 1); - for (i = 0, j = 0; i < (width & ~0x3); i += 4, j += 8) { - PNG_Do_Grayscale_A__8(i , j ); PNG_Do_Grayscale_A__8(i + 1, j + 2); - PNG_Do_Grayscale_A__8(i + 2, j + 4); PNG_Do_Grayscale_A__8(i + 3, j + 6); - } - for (; i < width; i++, j += 2) { PNG_Do_Grayscale_A__8(i, j); } -} - -static void Png_Expand_GRAYSCALE_A_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i; cc_uint8 rgb; /* NOTE: not optimised*/ - for (i = 0; i < width; i++) { - rgb = src[i * 4]; Bitmap_Set(dst[i], rgb, rgb, rgb, src[i * 4 + 2]); + for (; width >= 4; width -= 4) { + PNG_Do_Grayscale_A__8(); PNG_Do_Grayscale_A__8(); + PNG_Do_Grayscale_A__8(); PNG_Do_Grayscale_A__8(); } + for (; width > 0; width--) { PNG_Do_Grayscale_A__8(); } } static void Png_Expand_RGB_A_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; + /* Processed in forward order */ - for (i = 0, j = 0; i < (width & ~0x3); i += 4, j += 16) { - PNG_Do_RGB_A__8(i , j ); PNG_Do_RGB_A__8(i + 1, j + 4 ); - PNG_Do_RGB_A__8(i + 2, j + 8); PNG_Do_RGB_A__8(i + 3, j + 12); - } - for (; i < width; i++, j += 4) { PNG_Do_RGB_A__8(i, j); } -} - -static void Png_Expand_RGB_A_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) { - int i, j; /* NOTE: not optimised*/ - for (i = 0, j = 0; i < width; i++, j += 8) { - Bitmap_Set(dst[i], src[j], src[j + 2], src[j + 4], src[j + 6]); + for (; width >= 4; width -= 4) { + PNG_Do_RGB_A__8(); PNG_Do_RGB_A__8(); + PNG_Do_RGB_A__8(); PNG_Do_RGB_A__8(); } + for (; width > 0; width--) { PNG_Do_RGB_A__8(); } } static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) { @@ -279,14 +270,12 @@ static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) { case 2: return Png_Expand_GRAYSCALE_2; case 4: return Png_Expand_GRAYSCALE_4; case 8: return Png_Expand_GRAYSCALE_8; - case 16: return Png_Expand_GRAYSCALE_16; } return NULL; case PNG_COLOR_RGB: switch (bitsPerSample) { case 8: return Png_Expand_RGB_8; - case 16: return Png_Expand_RGB_16; } return NULL; @@ -302,21 +291,19 @@ static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) { case PNG_COLOR_GRAYSCALE_A: switch (bitsPerSample) { case 8: return Png_Expand_GRAYSCALE_A_8; - case 16: return Png_Expand_GRAYSCALE_A_16; } return NULL; case PNG_COLOR_RGB_A: switch (bitsPerSample) { case 8: return Png_Expand_RGB_A_8; - case 16: return Png_Expand_RGB_A_16; } return NULL; } return NULL; } -/* Sets alpha to 0 for any pixels in the bitmap whose RGB is same as col */ +/* Sets alpha to 0 for any pixels in the bitmap whose RGB is same as colorspace */ static void ComputeTransparency(struct Bitmap* bmp, BitmapCol col) { BitmapCol trnsRGB = col & BITMAPCOLOR_RGB_MASK; int x, y, width = bmp->width, height = bmp->height; @@ -330,11 +317,6 @@ static void ComputeTransparency(struct Bitmap* bmp, BitmapCol col) { } } -/* Most bits per sample is 16. Most samples per pixel is 4. Add 1 for filter byte. */ -/* Need to store both current and prior row, per PNG specification. */ -#define PNG_BUFFER_SIZE ((PNG_MAX_DIMS * 2 * 4 + 1) * 2) - -/* TODO: Test a lot of .png files and ensure output is right */ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { cc_uint8 tmp[64]; cc_uint32 dataSize, fourCC; @@ -342,26 +324,27 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /* header variables */ static const cc_uint8 samplesPerPixel[] = { 1, 0, 3, 1, 2, 0, 4 }; - cc_uint8 col, bitsPerSample, bytesPerPixel = 0; + cc_uint8 colorspace, bitsPerSample, bytesPerPixel = 0; Png_RowExpander rowExpander = NULL; cc_uint32 scanlineSize = 0, scanlineBytes = 0; /* palette data */ - BitmapCol trnsCol; + BitmapCol trnsColor; BitmapCol palette[PNG_PALETTE]; cc_uint32 i; /* idat state */ - cc_uint32 curY = 0, begY, rowY, endY; - cc_uint8 buffer[PNG_BUFFER_SIZE]; - cc_uint32 bufferRows, bufferLen; - cc_uint32 bufferIdx, read, left; - cc_bool initedBuffer = false; + cc_uint32 available = 0, rowY = 0; + cc_uint8 buffer[PNG_PALETTE * 3]; + cc_uint32 read, bufferIdx = 0; + cc_uint32 left, bufferLen = 0; + int curY; /* idat decompressor */ struct InflateState inflate; struct Stream compStream, datStream; struct ZLibHeader zlibHeader; + cc_uint8* data = NULL; bmp->width = 0; bmp->height = 0; bmp->scan0 = NULL; @@ -370,8 +353,8 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { if (res) return res; if (!Png_Detect(tmp, PNG_SIG_SIZE)) return PNG_ERR_INVALID_SIG; - col = 0xFF; /* Unknown colour space */ - trnsCol = BITMAPCOLOR_BLACK; + colorspace = 0xFF; /* Unknown colour space */ + trnsColor = BITMAPCOLOR_BLACK; for (i = 0; i < PNG_PALETTE; i++) { palette[i] = BITMAPCOLOR_BLACK; } Inflate_MakeStream2(&compStream, &inflate, stream); @@ -395,20 +378,25 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { if (bmp->width < 0 || bmp->width > PNG_MAX_DIMS) return PNG_ERR_TOO_WIDE; if (bmp->height < 0 || bmp->height > PNG_MAX_DIMS) return PNG_ERR_TOO_TALL; - bmp->scan0 = (BitmapCol*)Mem_TryAlloc(bmp->width * bmp->height, 4); - if (!bmp->scan0) return ERR_OUT_OF_MEMORY; + bitsPerSample = tmp[8]; colorspace = tmp[9]; + if (bitsPerSample == 16) return PNG_ERR_16BITSAMPLES; - bitsPerSample = tmp[8]; col = tmp[9]; - rowExpander = Png_GetExpander(col, bitsPerSample); + rowExpander = Png_GetExpander(colorspace, bitsPerSample); if (!rowExpander) return PNG_ERR_INVALID_COL_BPP; if (tmp[10] != 0) return PNG_ERR_COMP_METHOD; if (tmp[11] != 0) return PNG_ERR_FILTER; if (tmp[12] != 0) return PNG_ERR_INTERLACED; - bytesPerPixel = ((samplesPerPixel[col] * bitsPerSample) + 7) >> 3; - scanlineSize = ((samplesPerPixel[col] * bitsPerSample * bmp->width) + 7) >> 3; + bytesPerPixel = ((samplesPerPixel[colorspace] * bitsPerSample) + 7) >> 3; + scanlineSize = ((samplesPerPixel[colorspace] * bitsPerSample * bmp->width) + 7) >> 3; scanlineBytes = scanlineSize + 1; /* Add 1 byte for filter byte of each scanline */ + + data = Mem_TryAlloc(bmp->height, max(scanlineBytes, bmp->width * 4)); + bmp->scan0 = (BitmapCol*)data; + if (!bmp->scan0) return ERR_OUT_OF_MEMORY; + + bufferLen = bmp->height * scanlineBytes; } break; /* 11.2.3 PLTE Palette */ @@ -429,16 +417,15 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /* 11.3.2.1 tRNS Transparency */ case PNG_FourCC('t','R','N','S'): { - if (col == PNG_COLOR_GRAYSCALE) { + if (colorspace == PNG_COLOR_GRAYSCALE) { if (dataSize != 2) return PNG_ERR_TRANS_COUNT; res = Stream_Read(stream, buffer, dataSize); if (res) return res; /* RGB is always two bytes */ - /* TODO is this right for 16 bits per channel images? */ - trnsCol = BitmapCol_Make(buffer[1], buffer[1], buffer[1], 0); - } else if (col == PNG_COLOR_INDEXED) { + trnsColor = BitmapCol_Make(buffer[1], buffer[1], buffer[1], 0); + } else if (colorspace == PNG_COLOR_INDEXED) { if (dataSize > PNG_PALETTE) return PNG_ERR_TRANS_COUNT; res = Stream_Read(stream, buffer, dataSize); @@ -449,15 +436,14 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { palette[i] &= BITMAPCOLOR_RGB_MASK; /* set A to 0 */ palette[i] |= buffer[i] << BITMAPCOLOR_A_SHIFT; } - } else if (col == PNG_COLOR_RGB) { + } else if (colorspace == PNG_COLOR_RGB) { if (dataSize != 6) return PNG_ERR_TRANS_COUNT; res = Stream_Read(stream, buffer, dataSize); if (res) return res; /* R,G,B are always two bytes */ - /* TODO is this right for 16 bits per channel images? */ - trnsCol = BitmapCol_Make(buffer[1], buffer[3], buffer[5], 0); + trnsColor = BitmapCol_Make(buffer[1], buffer[3], buffer[5], 0); } else { return PNG_ERR_TRANS_INVALID; } @@ -472,59 +458,62 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { while (!zlibHeader.done) { if ((res = ZLibHeader_Read(&datStream, &zlibHeader))) return res; } - if (!bmp->scan0) return PNG_ERR_NO_DATA; - /* Initialise buffer for decoding */ - if (!initedBuffer) { - Mem_Set(buffer, 0, scanlineBytes); /* Prior row should be 0 per PNG spec */ - bufferIdx = scanlineBytes; - bufferRows = PNG_BUFFER_SIZE / scanlineBytes; - bufferLen = bufferRows * scanlineBytes; - initedBuffer = true; + if (!bmp->scan0) return PNG_ERR_NO_DATA; + if (rowY >= bmp->height) break; + left = bufferLen - bufferIdx; + + res = compStream.Read(&compStream, &data[bufferIdx], left, &read); + if (res) return res; + if (!read) break; + + available += read; + bufferIdx += read; + + /* Process all of the scanline(s) that have been fully decompressed */ + /* NOTE: Need to check height too, in case IDAT is corrupted and has extra data */ + for (; available >= scanlineBytes && rowY < bmp->height; rowY++, available -= scanlineBytes) { + cc_uint8* scanline = &data[rowY * scanlineBytes]; + if (scanline[0] > PNG_FILTER_PAETH) return PNG_ERR_INVALID_SCANLINE; + + if (rowY == 0) { + /* First row, prior is assumed as 0 */ + Png_ReconstructFirst(scanline[0], bytesPerPixel, &scanline[1], scanlineSize); + } else { + cc_uint8* prior = &data[(rowY - 1) * scanlineBytes]; + Png_Reconstruct(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize); + + /* With the RGBA colourspace, each scanline is (1 + width*4) bytes wide */ + /* Therefore once a row has been reconstructed, the prior row can be converted */ + /* immediately into the destination colour format */ + if (colorspace == PNG_COLOR_RGB_A) { + /* Prior line is no longer needed and can be overwritten now */ + rowExpander(bmp->width, palette, &prior[1], Bitmap_GetRow(bmp, rowY - 1)); + } + } + + /* Current line is also no longer needed and can be overwritten now */ + if (colorspace == PNG_COLOR_RGB_A && rowY == bmp->height - 1) { + rowExpander(bmp->width, palette, &scanline[1], Bitmap_GetRow(bmp, rowY)); + } } - while (curY < bmp->height) { - /* Need to leave one row in buffer untouched for storing prior scanline. Illustrated example of process: - * |=====| #-----| |-----| #-----| |-----| - * initial #-----| read 3 |-----| read 3 |-----| read 1 |-----| read 3 |-----| etc - * state |-----| -----> |-----| -----> |=====| -----> |-----| -----> |=====| - * |-----| |=====| #-----| |=====| #-----| - * - * (==== is prior scanline, # is current index in buffer) - * Having initial state this way allows doing two 'read 3' first time (assuming large idat chunks) - */ + /* Check if image fully decoded or not */ + if (bufferIdx != bufferLen) break; - begY = bufferIdx / scanlineBytes; - left = bufferLen - bufferIdx; - /* if row is at 0, last row in buffer is prior row */ - /* hence subtract a row, as don't want to overwrite it */ - if (begY == 0) left -= scanlineBytes; - - res = compStream.Read(&compStream, &buffer[bufferIdx], left, &read); - if (res) return res; - if (!read) break; - - bufferIdx += read; - endY = bufferIdx / scanlineBytes; - /* reached end of buffer, cycle back to start */ - if (bufferIdx == bufferLen) bufferIdx = 0; - - /* NOTE: Need to check curY too, in case IDAT is corrupted and has extra data */ - for (rowY = begY; rowY < endY && curY < bmp->height; rowY++, curY++) { - cc_uint32 priorY = rowY == 0 ? bufferRows : rowY; - cc_uint8* prior = &buffer[(priorY - 1) * scanlineBytes]; - cc_uint8* scanline = &buffer[rowY * scanlineBytes]; - - if (scanline[0] > PNG_FILTER_PAETH) return PNG_ERR_INVALID_SCANLINE; - Png_Reconstruct(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize); + /* With other colourspaces, the length of a scanline might be less than the width of a 8bpp image row */ + /* Therefore image expansion can only be done after all the rows have been reconstructed, and must */ + /* be done backwards to avoid overwriting any source data that has yet to be processed */ + /* This is slightly slower, but the majority of images ClassiCube encounters are RGBA anyways */ + if (colorspace != PNG_COLOR_RGB_A) { + for (curY = bmp->height - 1; curY >= 0; curY--) { + cc_uint8* scanline = &data[curY * scanlineBytes]; rowExpander(bmp->width, palette, &scanline[1], Bitmap_GetRow(bmp, curY)); } } - if (curY == bmp->height) { - if (!BitmapCol_A(trnsCol)) ComputeTransparency(bmp, trnsCol); - return 0; - } + if (!BitmapCol_A(trnsColor)) ComputeTransparency(bmp, trnsColor); + return 0; } break; /* 11.2.5 IEND Image trailer */ @@ -546,6 +535,7 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) { /*########################################################################################################################* *------------------------------------------------------PNG encoder--------------------------------------------------------* *#########################################################################################################################*/ +#ifdef CC_BUILD_FILESYSTEM static void Png_Filter(cc_uint8 filter, const cc_uint8* cur, const cc_uint8* prior, cc_uint8* best, int lineLen, int bpp) { /* 3 bytes per pixel constant */ cc_uint8 a, b, c; @@ -645,13 +635,13 @@ static void Png_EncodeRow(const cc_uint8* cur, const cc_uint8* prior, cc_uint8* best[0] = bestFilter; } -static BitmapCol* DefaultGetRow(struct Bitmap* bmp, int y) { return Bitmap_GetRow(bmp, y); } -cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, - Png_RowGetter getRow, cc_bool alpha) { +static BitmapCol* DefaultGetRow(struct Bitmap* bmp, int y, void* ctx) { return Bitmap_GetRow(bmp, y); } +static cc_result Png_EncodeCore(struct Bitmap* bmp, struct Stream* stream, cc_uint8* buffer, + Png_RowGetter getRow, cc_bool alpha, void* ctx) { cc_uint8 tmp[32]; - /* TODO: This should be * 4 for alpha (should switch to mem_alloc though) */ - cc_uint8 prevLine[PNG_MAX_DIMS * 3], curLine[PNG_MAX_DIMS * 3]; - cc_uint8 bestLine[PNG_MAX_DIMS * 3 + 1]; + cc_uint8* prevLine = buffer; + cc_uint8* curLine = buffer + (bmp->width * 4) * 1; + cc_uint8* bestLine = buffer + (bmp->width * 4) * 2; struct ZLibState zlState; struct Stream chunk, zlStream; @@ -691,7 +681,7 @@ cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, Mem_Set(prevLine, 0, lineSize); for (y = 0; y < bmp->height; y++) { - BitmapCol* src = getRow(bmp, y); + BitmapCol* src = getRow(bmp, y, ctx); cc_uint8* prev = (y & 1) == 0 ? prevLine : curLine; cc_uint8* cur = (y & 1) == 0 ? curLine : prevLine; @@ -702,7 +692,7 @@ cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, if ((res = Stream_Write(&zlStream, bestLine, lineSize + 1))) return res; } if ((res = zlStream.Close(&zlStream))) return res; - Stream_SetU32_BE(&tmp[0], chunk.Meta.CRC32.CRC32 ^ 0xFFFFFFFFUL); + Stream_SetU32_BE(&tmp[0], chunk.meta.crc32.crc32 ^ 0xFFFFFFFFUL); /* Write end chunk */ Stream_SetU32_BE(&tmp[4], 0); @@ -711,11 +701,30 @@ cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, if ((res = Stream_Write(stream, tmp, 16))) return res; /* Come back to fixup size of data in data chunk */ - /* TODO: Position instead of Length */ - if ((res = stream->Length(stream, &stream_end))) return res; + if ((res = stream->Position(stream, &stream_end))) return res; if ((res = stream->Seek(stream, stream_beg + 33))) return res; Stream_SetU32_BE(&tmp[0], (stream_end - stream_beg) - 57); if ((res = Stream_Write(stream, tmp, 4))) return res; return stream->Seek(stream, stream_end); } + +cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, + Png_RowGetter getRow, cc_bool alpha, void* ctx) { + cc_result res; + /* Add 1 for scanline filter type byter */ + cc_uint8* buffer = Mem_TryAlloc(3, bmp->width * 4 + 1); + if (!buffer) return ERR_NOT_SUPPORTED; + + res = Png_EncodeCore(bmp, stream, buffer, getRow, alpha, ctx); + Mem_Free(buffer); + return res; +} +#else +/* No point including encoding code when can't save screenshots anyways */ +cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, + Png_RowGetter getRow, cc_bool alpha, void* ctx) { + return ERR_NOT_SUPPORTED; +} +#endif + diff --git a/src/Bitmap.h b/src/Bitmap.h index 6089577e0..c8688cc09 100644 --- a/src/Bitmap.h +++ b/src/Bitmap.h @@ -13,7 +13,7 @@ typedef cc_uint32 BitmapCol; #define BITMAPCOLOR_G_SHIFT 8 #define BITMAPCOLOR_B_SHIFT 16 #define BITMAPCOLOR_A_SHIFT 24 -#elif defined CC_BUILD_3DS || defined CC_BUILD_N64 +#elif defined CC_BUILD_3DS || defined CC_BUILD_N64 || defined CC_BUILD_WIIU #define BITMAPCOLOR_R_SHIFT 24 #define BITMAPCOLOR_G_SHIFT 16 #define BITMAPCOLOR_B_SHIFT 8 @@ -89,7 +89,7 @@ CC_API void Bitmap_Scale(struct Bitmap* dst, struct Bitmap* src, /* Whether data starts with PNG format signature/identifier. */ cc_bool Png_Detect(const cc_uint8* data, cc_uint32 len); -typedef BitmapCol* (*Png_RowGetter)(struct Bitmap* bmp, int row); +typedef BitmapCol* (*Png_RowGetter)(struct Bitmap* bmp, int row, void* ctx); /* Decodes a bitmap in PNG format. Partially based off information from https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way @@ -100,5 +100,5 @@ CC_API cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream); /* getRow is optional. Can be used to modify how rows are encoded. (e.g. flip image) */ /* if alpha is non-zero, RGBA channels are saved, otherwise only RGB channels are. */ cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, - Png_RowGetter getRow, cc_bool alpha); + Png_RowGetter getRow, cc_bool alpha, void* ctx); #endif diff --git a/src/Block.c b/src/Block.c index 635853c54..a5f82971a 100644 --- a/src/Block.c +++ b/src/Block.c @@ -351,7 +351,13 @@ static void Block_CalcCulling(BlockID block, BlockID other) { cc_bool occludedX, occludedY, occludedZ, bothLiquid; int f; - /* Some blocks may not cull 'other' block, in which case just skip per-face check */ + /* Fast path: Full opaque neighbouring blocks will always have all shared faces hidden */ + if (Blocks.FullOpaque[block] && Blocks.FullOpaque[other]) { + Blocks.Hidden[(block * BLOCK_COUNT) + other] = 0x3F; + return; + } + + /* Some blocks may not cull 'other' block, in which case just skip detailed check */ /* e.g. sprite blocks, default leaves, will not cull any other blocks */ if (!Block_MightCull(block, other)) { Blocks.Hidden[(block * BLOCK_COUNT) + other] = 0; @@ -386,6 +392,7 @@ static void Block_CalcCulling(BlockID block, BlockID other) { /* Updates culling data of all blocks */ static void Block_UpdateAllCulling(void) { int block, neighbour; + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) { Block_CalcStretch((BlockID)block); for (neighbour = BLOCK_AIR; neighbour < BLOCK_COUNT; neighbour++) { @@ -511,7 +518,8 @@ int Block_FindID(const cc_string* name) { cc_string blockName; int block; - for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) { + for (block = BLOCK_AIR; block < BLOCK_COUNT; block++) + { blockName = Block_UNSAFE_GetName(block); if (String_CaselessEquals(&blockName, name)) return block; } @@ -616,8 +624,8 @@ static int GetRotated(cc_string* name, int offset) { } static int RotateCorner(cc_string* name, int offset) { - float x = Game_SelectedPos.Intersect.x - (float)Game_SelectedPos.TranslatedPos.x; - float z = Game_SelectedPos.Intersect.z - (float)Game_SelectedPos.TranslatedPos.z; + float x = Game_SelectedPos.intersect.x - (float)Game_SelectedPos.translatedPos.x; + float z = Game_SelectedPos.intersect.z - (float)Game_SelectedPos.translatedPos.z; if (x < 0.5f && z < 0.5f) { AutoRotate_Insert(name, offset, "-NW"); @@ -632,7 +640,7 @@ static int RotateCorner(cc_string* name, int offset) { } static int RotateVertical(cc_string* name, int offset) { - float y = Game_SelectedPos.Intersect.y - (float)Game_SelectedPos.TranslatedPos.y; + float y = Game_SelectedPos.intersect.y - (float)Game_SelectedPos.translatedPos.y; if (y >= 0.5f) { AutoRotate_Insert(name, offset, "-U"); @@ -644,7 +652,7 @@ static int RotateVertical(cc_string* name, int offset) { static int RotateFence(cc_string* name, int offset) { /* Fence type blocks */ - float yaw = Math_ClampAngle(LocalPlayer_Instance.Base.Yaw); + float yaw = Math_ClampAngle(Entities.CurPlayer->Base.Yaw); if (yaw < 45.0f || (yaw >= 135.0f && yaw < 225.0f) || yaw > 315.0f) { AutoRotate_Insert(name, offset, "-WE"); @@ -656,7 +664,7 @@ static int RotateFence(cc_string* name, int offset) { static int RotatePillar(cc_string* name, int offset) { /* Thin pillar type blocks */ - Face face = Game_SelectedPos.Closest; + Face face = Game_SelectedPos.closest; if (face == FACE_YMAX || face == FACE_YMIN) { AutoRotate_Insert(name, offset, "-UD"); @@ -669,7 +677,7 @@ static int RotatePillar(cc_string* name, int offset) { } static int RotateDirection(cc_string* name, int offset) { - float yaw = Math_ClampAngle(LocalPlayer_Instance.Base.Yaw); + float yaw = Math_ClampAngle(Entities.CurPlayer->Base.Yaw); if (yaw >= 45.0f && yaw < 135.0f) { AutoRotate_Insert(name, offset, "-E"); diff --git a/src/BlockPhysics.c b/src/BlockPhysics.c index ca07827d6..bedeef71d 100644 --- a/src/BlockPhysics.c +++ b/src/BlockPhysics.c @@ -527,7 +527,6 @@ void Physics_Init(void) { Physics.OnRandomTick[BLOCK_SAND] = Physics_DoFalling; Physics.OnRandomTick[BLOCK_GRAVEL] = Physics_DoFalling; - Physics.OnPlace[BLOCK_SAPLING] = Physics_HandleSapling; Physics.OnRandomTick[BLOCK_SAPLING] = Physics_HandleSapling; Physics.OnRandomTick[BLOCK_DIRT] = Physics_HandleDirt; Physics.OnRandomTick[BLOCK_GRASS] = Physics_HandleGrass; diff --git a/src/Builder.c b/src/Builder.c index a6ce1a809..a6b1b0d19 100644 --- a/src/Builder.c +++ b/src/Builder.c @@ -39,9 +39,12 @@ static void (*Builder_PostPrepareChunk)(void); /* Contains state for vertices for a portion of a chunk mesh (vertices that are in a 1D atlas) */ struct Builder1DPart { - struct VertexTextured* fVertices[FACE_COUNT]; - int fCount[FACE_COUNT]; - int sCount, sOffset, sAdvance; + /* Union to save on memory, since chunk building is divided into counting then building phases */ + union FaceData { + struct VertexTextured* vertices[FACE_COUNT]; + int count[FACE_COUNT]; + } faces; + int sCount, sOffset; }; /* Part builder data, for both normal and translucent parts. @@ -51,19 +54,25 @@ static struct VertexTextured* Builder_Vertices; static int Builder1DPart_VerticesCount(struct Builder1DPart* part) { int i, count = part->sCount; - for (i = 0; i < FACE_COUNT; i++) { count += part->fCount[i]; } + for (i = 0; i < FACE_COUNT; i++) { count += part->faces.count[i]; } return count; } static int Builder1DPart_CalcOffsets(struct Builder1DPart* part, int offset) { - int i; - part->sOffset = offset; - part->sAdvance = part->sCount >> 2; + int i, counts[FACE_COUNT]; + part->sOffset = offset; + + /* Have to copy first due to count and vertices fields being a union */ + for (i = 0; i < FACE_COUNT; i++) + { + counts[i] = part->faces.count[i]; + } offset += part->sCount; - for (i = 0; i < FACE_COUNT; i++) { - part->fVertices[i] = &Builder_Vertices[offset]; - offset += part->fCount[i]; + for (i = 0; i < FACE_COUNT; i++) + { + part->faces.vertices[i] = &Builder_Vertices[offset]; + offset += counts[i]; } return offset; } @@ -90,54 +99,50 @@ static void AddVertices(BlockID block, Face face) { int baseOffset = (Blocks.Draw[block] == DRAW_TRANSLUCENT) * ATLAS1D_MAX_ATLASES; int i = Atlas1D_Index(Block_Tex(block, face)); struct Builder1DPart* part = &Builder_Parts[baseOffset + i]; - part->fCount[face] += 4; + part->faces.count[face] += 4; } #ifdef CC_BUILD_GL11 static void BuildPartVbs(struct ChunkPartInfo* info) { /* Sprites vertices are stored before chunk face sides */ - int i, count, offset = info->Offset + info->SpriteCount; + int i, count, offset = info->offset + info->spriteCount; for (i = 0; i < FACE_COUNT; i++) { - count = info->Counts[i]; + count = info->counts[i]; if (count) { - info->Vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count); + info->vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count); offset += count; } else { - info->Vbs[i] = 0; + info->vbs[i] = 0; } } - count = info->SpriteCount; - offset = info->Offset; + count = info->spriteCount; + offset = info->offset; if (count) { - info->Vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count); + info->vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count); } else { - info->Vbs[i] = 0; + info->vbs[i] = 0; } } #endif -static void SetPartInfo(struct Builder1DPart* part, int* offset, struct ChunkPartInfo* info, cc_bool* hasParts) { +static cc_bool SetPartInfo(struct Builder1DPart* part, int* offset, struct ChunkPartInfo* info) { int vCount = Builder1DPart_VerticesCount(part); - info->Offset = -1; - if (!vCount) return; + info->offset = -1; + if (!vCount) return false; - info->Offset = *offset; + info->offset = *offset; *offset += vCount; - *hasParts = true; - info->Counts[FACE_XMIN] = part->fCount[FACE_XMIN]; - info->Counts[FACE_XMAX] = part->fCount[FACE_XMAX]; - info->Counts[FACE_ZMIN] = part->fCount[FACE_ZMIN]; - info->Counts[FACE_ZMAX] = part->fCount[FACE_ZMAX]; - info->Counts[FACE_YMIN] = part->fCount[FACE_YMIN]; - info->Counts[FACE_YMAX] = part->fCount[FACE_YMAX]; - info->SpriteCount = part->sCount; - -#ifdef CC_BUILD_GL11 - BuildPartVbs(info); -#endif + info->counts[FACE_XMIN] = part->faces.count[FACE_XMIN]; + info->counts[FACE_XMAX] = part->faces.count[FACE_XMAX]; + info->counts[FACE_ZMIN] = part->faces.count[FACE_ZMIN]; + info->counts[FACE_ZMAX] = part->faces.count[FACE_ZMAX]; + info->counts[FACE_YMIN] = part->faces.count[FACE_YMIN]; + info->counts[FACE_YMAX] = part->faces.count[FACE_YMAX]; + info->spriteCount = part->sCount; + return true; } @@ -328,7 +333,33 @@ static cc_bool ReadBorderChunkData(int x1, int y1, int z1, cc_bool* outAllAir) { return false; } -static cc_bool BuildChunk(int x1, int y1, int z1, struct ChunkInfo* info) { +static void OutputChunkPartsMeta(int x, int y, int z, struct ChunkInfo* info) { + cc_bool hasNorm, hasTran; + int partsIndex; + int i, j, curIdx, offset; + + partsIndex = World_ChunkPack(x >> CHUNK_SHIFT, y >> CHUNK_SHIFT, z >> CHUNK_SHIFT); + offset = 0; + hasNorm = false; + hasTran = false; + + for (i = 0; i < MapRenderer_1DUsedCount; i++) { + j = i + ATLAS1D_MAX_ATLASES; + curIdx = partsIndex + i * World.ChunksCount; + + hasNorm |= SetPartInfo(&Builder_Parts[i], &offset, &MapRenderer_PartsNormal[curIdx]); + hasTran |= SetPartInfo(&Builder_Parts[j], &offset, &MapRenderer_PartsTranslucent[curIdx]); + } + + if (hasNorm) { + info->normalParts = &MapRenderer_PartsNormal[partsIndex]; + } + if (hasTran) { + info->translucentParts = &MapRenderer_PartsTranslucent[partsIndex]; + } +} + +void Builder_MakeChunk(struct ChunkInfo* info) { BlockID chunk[EXTCHUNK_SIZE_3]; cc_uint8 counts[CHUNK_SIZE_3 * FACE_COUNT]; int bitFlags[EXTCHUNK_SIZE_3]; @@ -337,6 +368,7 @@ static cc_bool BuildChunk(int x1, int y1, int z1, struct ChunkInfo* info) { int xMax, yMax, zMax, totalVerts; int cIndex, index; int x, y, z, xx, yy, zz; + int x1 = info->centreX - 8, y1 = info->centreY - 8, z1 = info->centreZ - 8; Builder_Chunk = chunk; Builder_Counts = counts; @@ -355,9 +387,9 @@ static cc_bool BuildChunk(int x1, int y1, int z1, struct ChunkInfo* info) { allSolid = ReadChunkData(x1, y1, z1, &allAir); } - info->AllAir = allAir; - if (allAir || allSolid) return false; - Lighting.LightHint(x1 - 1, y1 - 1, z1 - 1); + info->allAir = allAir; + if (allAir || allSolid) return; + Lighting.LightHint(x1 - 1, z1 - 1); Mem_Set(counts, 1, CHUNK_SIZE_3 * FACE_COUNT); xMax = min(World.Width, x1 + CHUNK_SIZE); @@ -368,11 +400,17 @@ static cc_bool BuildChunk(int x1, int y1, int z1, struct ChunkInfo* info) { PrepareChunk(x1, y1, z1); totalVerts = Builder_TotalVerticesCount(); - if (!totalVerts) return false; + if (!totalVerts) return; + + OutputChunkPartsMeta(x1, y1, z1, info); +#ifdef OCCLUSION + if (info.NormalParts != null || info.TranslucentParts != null) + info.occlusionFlags = (cc_uint8)ComputeOcclusion(); +#endif #ifndef CC_BUILD_GL11 /* add an extra element to fix crashing on some GPUs */ - Builder_Vertices = (struct VertexTextured*)Gfx_RecreateAndLockVb(&info->Vb, + Builder_Vertices = (struct VertexTextured*)Gfx_RecreateAndLockVb(&info->vb, VERTEX_FORMAT_TEXTURED, totalVerts + 1); #else /* NOTE: Relies on assumption vb is ignored by GL11 Gfx_LockVb implementation */ @@ -397,44 +435,17 @@ static cc_bool BuildChunk(int x1, int y1, int z1, struct ChunkInfo* info) { } } -#ifndef CC_BUILD_GL11 - Gfx_UnlockVb(info->Vb); -#endif - return true; -} +#ifdef CC_BUILD_GL11 + cIndex = World_ChunkPack(x1 >> CHUNK_SHIFT, y1 >> CHUNK_SHIFT, z1 >> CHUNK_SHIFT); -void Builder_MakeChunk(struct ChunkInfo* info) { - int x = info->CentreX - 8, y = info->CentreY - 8, z = info->CentreZ - 8; - cc_bool hasMesh, hasNorm, hasTran; - int partsIndex; - int i, j, curIdx, offset; + for (index = 0; index < MapRenderer_1DUsedCount; index++) { + int curIdx = cIndex + index * World.ChunksCount; - hasMesh = BuildChunk(x, y, z, info); - if (!hasMesh) return; - - partsIndex = World_ChunkPack(x >> CHUNK_SHIFT, y >> CHUNK_SHIFT, z >> CHUNK_SHIFT); - offset = 0; - hasNorm = false; - hasTran = false; - - for (i = 0; i < MapRenderer_1DUsedCount; i++) { - j = i + ATLAS1D_MAX_ATLASES; - curIdx = partsIndex + i * World.ChunksCount; - - SetPartInfo(&Builder_Parts[i], &offset, &MapRenderer_PartsNormal[curIdx], &hasNorm); - SetPartInfo(&Builder_Parts[j], &offset, &MapRenderer_PartsTranslucent[curIdx], &hasTran); + BuildPartVbs(&MapRenderer_PartsNormal[curIdx]); + BuildPartVbs(&MapRenderer_PartsTranslucent[curIdx]); } - - if (hasNorm) { - info->NormalParts = &MapRenderer_PartsNormal[partsIndex]; - } - if (hasTran) { - info->TranslucentParts = &MapRenderer_PartsTranslucent[partsIndex]; - } - -#ifdef OCCLUSION - if (info.NormalParts != null || info.TranslucentParts != null) - info.occlusionFlags = (cc_uint8)ComputeOcclusion(); +#else + Gfx_UnlockVb(info->vb); #endif } @@ -466,12 +477,12 @@ static void DefaultPostStretchChunk(void) { static RNGState spriteRng; static void Builder_DrawSprite(int x, int y, int z) { struct Builder1DPart* part; - struct VertexTextured v; + struct VertexTextured* v; cc_uint8 offsetType; cc_bool bright; + PackedCol color; TextureLoc loc; float v1, v2; - int index; float X, Y, Z; float valX, valY, valZ; @@ -501,36 +512,36 @@ static void Builder_DrawSprite(int x, int y, int z) { bright = Blocks.Brightness[Builder_Block]; part = &Builder_Parts[Atlas1D_Index(loc)]; - v.Col = bright ? PACKEDCOL_WHITE : Lighting.Color_Fast(x, y, z); - Block_Tint(v.Col, Builder_Block); + color = bright ? PACKEDCOL_WHITE : Lighting.Color_Sprite_Fast(x, y, z); + Block_Tint(color, Builder_Block); /* Draw Z axis */ - index = part->sOffset; - v.x = x1; v.y = y1; v.z = z1; v.U = s_u2; v.V = v2; Builder_Vertices[index + 0] = v; - v.y = y2; v.V = v1; Builder_Vertices[index + 1] = v; - v.x = x2; v.z = z2; v.U = s_u1; Builder_Vertices[index + 2] = v; - v.y = y1; v.V = v2; Builder_Vertices[index + 3] = v; + v = &Builder_Vertices[part->sOffset]; + v->x = x1; v->y = y1; v->z = z1; v->Col = color; v->U = s_u2; v->V = v2; v++; + v->x = x1; v->y = y2; v->z = z1; v->Col = color; v->U = s_u2; v->V = v1; v++; + v->x = x2; v->y = y2; v->z = z2; v->Col = color; v->U = s_u1; v->V = v1; v++; + v->x = x2; v->y = y1; v->z = z2; v->Col = color; v->U = s_u1; v->V = v2; v++; /* Draw Z axis mirrored */ - index += part->sAdvance; - v.x = x2; v.y = y1; v.z = z2; v.U = s_u2; Builder_Vertices[index + 0] = v; - v.y = y2; v.V = v1; Builder_Vertices[index + 1] = v; - v.x = x1; v.z = z1; v.U = s_u1; Builder_Vertices[index + 2] = v; - v.y = y1; v.V = v2; Builder_Vertices[index + 3] = v; + v -= 4; v += part->sCount >> 2; + v->x = x2; v->y = y1; v->z = z2; v->Col = color; v->U = s_u2; v->V = v2; v++; + v->x = x2; v->y = y2; v->z = z2; v->Col = color; v->U = s_u2; v->V = v1; v++; + v->x = x1; v->y = y2; v->z = z1; v->Col = color; v->U = s_u1; v->V = v1; v++; + v->x = x1; v->y = y1; v->z = z1; v->Col = color; v->U = s_u1; v->V = v2; v++; /* Draw X axis */ - index += part->sAdvance; - v.x = x1; v.y = y1; v.z = z2; v.U = s_u2; Builder_Vertices[index + 0] = v; - v.y = y2; v.V = v1; Builder_Vertices[index + 1] = v; - v.x = x2; v.z = z1; v.U = s_u1; Builder_Vertices[index + 2] = v; - v.y = y1; v.V = v2; Builder_Vertices[index + 3] = v; + v -= 4; v += part->sCount >> 2; + v->x = x1; v->y = y1; v->z = z2; v->Col = color; v->U = s_u2; v->V = v2; v++; + v->x = x1; v->y = y2; v->z = z2; v->Col = color; v->U = s_u2; v->V = v1; v++; + v->x = x2; v->y = y2; v->z = z1; v->Col = color; v->U = s_u1; v->V = v1; v++; + v->x = x2; v->y = y1; v->z = z1; v->Col = color; v->U = s_u1; v->V = v2; v++; /* Draw X axis mirrored */ - index += part->sAdvance; - v.x = x2; v.y = y1; v.z = z1; v.U = s_u2; Builder_Vertices[index + 0] = v; - v.y = y2; v.V = v1; Builder_Vertices[index + 1] = v; - v.x = x1; v.z = z2; v.U = s_u1; Builder_Vertices[index + 2] = v; - v.y = y1; v.V = v2; Builder_Vertices[index + 3] = v; + v -= 4; v += part->sCount >> 2; + v->x = x2; v->y = y1; v->z = z1; v->Col = color; v->U = s_u2; v->V = v2; v++; + v->x = x2; v->y = y2; v->z = z1; v->Col = color; v->U = s_u2; v->V = v1; v++; + v->x = x1; v->y = y2; v->z = z2; v->Col = color; v->U = s_u1; v->V = v1; v++; + v->x = x1; v->y = y1; v->z = z2; v->Col = color; v->U = s_u1; v->V = v2; v++; part->sOffset += 4; } @@ -676,7 +687,7 @@ static void NormalBuilder_RenderBlock(int index, int x, int y, int z) { col = fullBright ? PACKEDCOL_WHITE : x >= offset ? Lighting.Color_XSide_Fast(x - offset, y, z) : Env.SunXSide; - Drawer_XMin(count_XMin, col, loc, &part->fVertices[FACE_XMIN]); + Drawer_XMin(count_XMin, col, loc, &part->faces.vertices[FACE_XMIN]); } if (count_XMax) { @@ -686,7 +697,7 @@ static void NormalBuilder_RenderBlock(int index, int x, int y, int z) { col = fullBright ? PACKEDCOL_WHITE : x <= (World.MaxX - offset) ? Lighting.Color_XSide_Fast(x + offset, y, z) : Env.SunXSide; - Drawer_XMax(count_XMax, col, loc, &part->fVertices[FACE_XMAX]); + Drawer_XMax(count_XMax, col, loc, &part->faces.vertices[FACE_XMAX]); } if (count_ZMin) { @@ -696,7 +707,7 @@ static void NormalBuilder_RenderBlock(int index, int x, int y, int z) { col = fullBright ? PACKEDCOL_WHITE : z >= offset ? Lighting.Color_ZSide_Fast(x, y, z - offset) : Env.SunZSide; - Drawer_ZMin(count_ZMin, col, loc, &part->fVertices[FACE_ZMIN]); + Drawer_ZMin(count_ZMin, col, loc, &part->faces.vertices[FACE_ZMIN]); } if (count_ZMax) { @@ -706,7 +717,7 @@ static void NormalBuilder_RenderBlock(int index, int x, int y, int z) { col = fullBright ? PACKEDCOL_WHITE : z <= (World.MaxZ - offset) ? Lighting.Color_ZSide_Fast(x, y, z + offset) : Env.SunZSide; - Drawer_ZMax(count_ZMax, col, loc, &part->fVertices[FACE_ZMAX]); + Drawer_ZMax(count_ZMax, col, loc, &part->faces.vertices[FACE_ZMAX]); } if (count_YMin) { @@ -715,7 +726,7 @@ static void NormalBuilder_RenderBlock(int index, int x, int y, int z) { part = &Builder_Parts[baseOffset + Atlas1D_Index(loc)]; col = fullBright ? PACKEDCOL_WHITE : Lighting.Color_YMin_Fast(x, y - offset, z); - Drawer_YMin(count_YMin, col, loc, &part->fVertices[FACE_YMIN]); + Drawer_YMin(count_YMin, col, loc, &part->faces.vertices[FACE_YMIN]); } if (count_YMax) { @@ -723,8 +734,8 @@ static void NormalBuilder_RenderBlock(int index, int x, int y, int z) { offset = (lightFlags >> FACE_YMAX) & 1; part = &Builder_Parts[baseOffset + Atlas1D_Index(loc)]; - col = fullBright ? PACKEDCOL_WHITE : Lighting.Color_Fast(x, y + offset, z); - Drawer_YMax(count_YMax, col, loc, &part->fVertices[FACE_YMAX]); + col = fullBright ? PACKEDCOL_WHITE : Lighting.Color_YMax_Fast(x, y + offset, z); + Drawer_YMax(count_YMax, col, loc, &part->faces.vertices[FACE_YMAX]); } } @@ -963,7 +974,7 @@ static void Adv_DrawXMin(int count) { col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint); } - vertices = part->fVertices[FACE_XMIN]; + vertices = part->faces.vertices[FACE_XMIN]; v.x = adv_x1; if (aY0_Z0 + aY1_Z1 > aY0_Z1 + aY1_Z0) { v.y = adv_y2; v.z = adv_z1; v.U = u1; v.V = v1; v.Col = col1_0; *vertices++ = v; @@ -976,7 +987,7 @@ static void Adv_DrawXMin(int count) { v.y = adv_y1; v.V = v2; v.Col = col0_0; *vertices++ = v; v.z = adv_z2 + (count - 1); v.U = u2; v.Col = col0_1; *vertices++ = v; } - part->fVertices[FACE_XMIN] = vertices; + part->faces.vertices[FACE_XMIN] = vertices; } static void Adv_DrawXMax(int count) { @@ -1005,7 +1016,7 @@ static void Adv_DrawXMax(int count) { col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint); } - vertices = part->fVertices[FACE_XMAX]; + vertices = part->faces.vertices[FACE_XMAX]; v.x = adv_x2; if (aY0_Z0 + aY1_Z1 > aY0_Z1 + aY1_Z0) { v.y = adv_y2; v.z = adv_z1; v.U = u1; v.V = v1; v.Col = col1_0; *vertices++ = v; @@ -1018,7 +1029,7 @@ static void Adv_DrawXMax(int count) { v.z = adv_z1; v.U = u1; v.Col = col0_0; *vertices++ = v; v.y = adv_y2; v.V = v1; v.Col = col1_0; *vertices++ = v; } - part->fVertices[FACE_XMAX] = vertices; + part->faces.vertices[FACE_XMAX] = vertices; } static void Adv_DrawZMin(int count) { @@ -1047,7 +1058,7 @@ static void Adv_DrawZMin(int count) { col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint); } - vertices = part->fVertices[FACE_ZMIN]; + vertices = part->faces.vertices[FACE_ZMIN]; v.z = adv_z1; if (aX1_Y1 + aX0_Y0 > aX0_Y1 + aX1_Y0) { v.x = adv_x2 + (count - 1); v.y = adv_y1; v.U = u2; v.V = v2; v.Col = col1_0; *vertices++ = v; @@ -1060,7 +1071,7 @@ static void Adv_DrawZMin(int count) { v.x = adv_x2 + (count - 1); v.U = u2; v.Col = col1_1; *vertices++ = v; v.y = adv_y1; v.V = v2; v.Col = col1_0; *vertices++ = v; } - part->fVertices[FACE_ZMIN] = vertices; + part->faces.vertices[FACE_ZMIN] = vertices; } static void Adv_DrawZMax(int count) { @@ -1089,7 +1100,7 @@ static void Adv_DrawZMax(int count) { col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint); } - vertices = part->fVertices[FACE_ZMAX]; + vertices = part->faces.vertices[FACE_ZMAX]; v.z = adv_z2; if (aX1_Y1 + aX0_Y0 > aX0_Y1 + aX1_Y0) { v.x = adv_x1; v.y = adv_y2; v.U = u1; v.V = v1; v.Col = col0_1; *vertices++ = v; @@ -1102,7 +1113,7 @@ static void Adv_DrawZMax(int count) { v.y = adv_y1; v.V = v2; v.Col = col0_0; *vertices++ = v; v.x = adv_x2 + (count - 1); v.U = u2; v.Col = col1_0; *vertices++ = v; } - part->fVertices[FACE_ZMAX] = vertices; + part->faces.vertices[FACE_ZMAX] = vertices; } static void Adv_DrawYMin(int count) { @@ -1131,7 +1142,7 @@ static void Adv_DrawYMin(int count) { col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint); } - vertices = part->fVertices[FACE_YMIN]; + vertices = part->faces.vertices[FACE_YMIN]; v.y = adv_y1; if (aX0_Z1 + aX1_Z0 > aX0_Z0 + aX1_Z1) { v.x = adv_x2 + (count - 1); v.z = adv_z2; v.U = u2; v.V = v2; v.Col = col1_1; *vertices++ = v; @@ -1144,7 +1155,7 @@ static void Adv_DrawYMin(int count) { v.x = adv_x2 + (count - 1); v.U = u2; v.Col = col1_0; *vertices++ = v; v.z = adv_z2; v.V = v2; v.Col = col1_1; *vertices++ = v; } - part->fVertices[FACE_YMIN] = vertices; + part->faces.vertices[FACE_YMIN] = vertices; } static void Adv_DrawYMax(int count) { @@ -1173,7 +1184,7 @@ static void Adv_DrawYMax(int count) { col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint); } - vertices = part->fVertices[FACE_YMAX]; + vertices = part->faces.vertices[FACE_YMAX]; v.y = adv_y2; if (aX0_Z0 + aX1_Z1 > aX0_Z1 + aX1_Z0) { v.x = adv_x2 + (count - 1); v.z = adv_z1; v.U = u2; v.V = v1; v.Col = col1_0; *vertices++ = v; @@ -1186,7 +1197,7 @@ static void Adv_DrawYMax(int count) { v.x = adv_x2 + (count - 1); v.U = u2; v.Col = col1_1; *vertices++ = v; v.z = adv_z1; v.V = v1; v.Col = col1_0; *vertices++ = v; } - part->fVertices[FACE_YMAX] = vertices; + part->faces.vertices[FACE_YMAX] = vertices; } static void Adv_RenderBlock(int index, int x, int y, int z) { diff --git a/src/Camera.c b/src/Camera.c index 188f5b946..2133ab942 100644 --- a/src/Camera.c +++ b/src/Camera.c @@ -22,15 +22,15 @@ static void Camera_OnRawMovement(float deltaX, float deltaY) { cam_deltaX += deltaX; cam_deltaY += deltaY; } -void Camera_KeyLookUpdate(double delta) { +void Camera_KeyLookUpdate(float delta) { if (Gui.InputGrab) return; /* divide by 25 to have reasonable sensitivity for default mouse sens */ - delta = (Camera.Sensitivity / 25.0f) * (1000 * delta); + float amount = (Camera.Sensitivity / 25.0f) * (1000 * delta); - if (KeyBind_IsPressed(KEYBIND_LOOK_UP)) cam_deltaY -= delta; - if (KeyBind_IsPressed(KEYBIND_LOOK_DOWN)) cam_deltaY += delta; - if (KeyBind_IsPressed(KEYBIND_LOOK_LEFT)) cam_deltaX -= delta; - if (KeyBind_IsPressed(KEYBIND_LOOK_RIGHT)) cam_deltaX += delta; + if (KeyBind_IsPressed(KEYBIND_LOOK_UP)) cam_deltaY -= amount; + if (KeyBind_IsPressed(KEYBIND_LOOK_DOWN)) cam_deltaY += amount; + if (KeyBind_IsPressed(KEYBIND_LOOK_LEFT)) cam_deltaX -= amount; + if (KeyBind_IsPressed(KEYBIND_LOOK_RIGHT)) cam_deltaX += amount; } /*########################################################################################################################* @@ -42,24 +42,23 @@ static void PerspectiveCamera_GetProjection(struct Matrix* proj) { Gfx_CalcPerspectiveMatrix(proj, fovy, aspectRatio, (float)Game_ViewDistance); } -static void PerspectiveCamera_GetView(struct Matrix* mat) { +static void PerspectiveCamera_GetView(struct LocalPlayer* p, struct Matrix* mat) { Vec3 pos = Camera.CurrentPos; - Vec2 rot = Camera.Active->GetOrientation(); + Vec2 rot = Camera.Active->GetOrientation(p); Matrix_LookRot(mat, pos, rot); Matrix_MulBy(mat, &Camera.TiltM); } -static void PerspectiveCamera_GetPickedBlock(struct RayTracer* t) { - struct Entity* p = &LocalPlayer_Instance.Base; - Vec3 dir = Vec3_GetDirVector(p->Yaw * MATH_DEG2RAD, p->Pitch * MATH_DEG2RAD + Camera.TiltPitch); - Vec3 eyePos = Entity_GetEyePosition(p); - float reach = LocalPlayer_Instance.ReachDistance; - Picking_CalcPickedBlock(&eyePos, &dir, reach, t); +static void PerspectiveCamera_GetPickedBlock(struct LocalPlayer* p, struct RayTracer* t) { + struct Entity* e = &p->Base; + Vec3 dir = Vec3_GetDirVector(e->Yaw * MATH_DEG2RAD, e->Pitch * MATH_DEG2RAD + Camera.TiltPitch); + Vec3 eyePos = Entity_GetEyePosition(e); + Picking_CalcPickedBlock(&eyePos, &dir, p->ReachDistance, t); } #define CAMERA_SENSI_FACTOR (0.0002f / 3.0f * MATH_RAD2DEG) -static Vec2 PerspectiveCamera_GetMouseDelta(double delta) { +static Vec2 PerspectiveCamera_GetMouseDelta(float delta) { float sensitivity = CAMERA_SENSI_FACTOR * Camera.Sensitivity; static float speedX, speedY, newSpeedX, newSpeedY, accelX, accelY; Vec2 v; @@ -67,8 +66,8 @@ static Vec2 PerspectiveCamera_GetMouseDelta(double delta) { if (Camera.Smooth) { accelX = (cam_deltaX - speedX) * 35 / Camera.Mass; accelY = (cam_deltaY - speedY) * 35 / Camera.Mass; - newSpeedX = accelX * (float)delta + speedX; - newSpeedY = accelY * (float)delta + speedY; + newSpeedX = accelX * delta + speedX; + newSpeedY = accelY * delta + speedY; /* High acceleration means velocity overshoots the correct position on low FPS, */ /* causing wiggling. If newSpeed has opposite sign of speed, set speed to 0 */ @@ -86,8 +85,8 @@ static Vec2 PerspectiveCamera_GetMouseDelta(double delta) { return v; } -static void PerspectiveCamera_UpdateMouseRotation(double delta) { - struct Entity* e = &LocalPlayer_Instance.Base; +static void PerspectiveCamera_UpdateMouseRotation(struct LocalPlayer* p, float delta) { + struct Entity* e = &p->Base; struct LocationUpdate update; Vec2 rot = PerspectiveCamera_GetMouseDelta(delta); @@ -108,15 +107,14 @@ static void PerspectiveCamera_UpdateMouseRotation(double delta) { e->VTABLE->SetLocation(e, &update); } -static void PerspectiveCamera_UpdateMouse(double delta) { +static void PerspectiveCamera_UpdateMouse(struct LocalPlayer* p, float delta) { if (!Gui.InputGrab && Window_Main.Focused) Window_UpdateRawMouse(); - PerspectiveCamera_UpdateMouseRotation(delta); + PerspectiveCamera_UpdateMouseRotation(p, delta); cam_deltaX = 0; cam_deltaY = 0; } -static void PerspectiveCamera_CalcViewBobbing(float t, float velTiltScale) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static void PerspectiveCamera_CalcViewBobbing(struct LocalPlayer* p, float t, float velTiltScale) { struct Entity* e = &p->Base; struct Matrix tiltY, velX; @@ -148,23 +146,23 @@ static void PerspectiveCamera_CalcViewBobbing(float t, float velTiltScale) { /*########################################################################################################################* *---------------------------------------------------First person camera---------------------------------------------------* *#########################################################################################################################*/ -static Vec2 FirstPersonCamera_GetOrientation(void) { - struct Entity* p = &LocalPlayer_Instance.Base; +static Vec2 FirstPersonCamera_GetOrientation(struct LocalPlayer* p) { + struct Entity* e = &p->Base; Vec2 v; - v.x = p->Yaw * MATH_DEG2RAD; - v.y = p->Pitch * MATH_DEG2RAD; + v.x = e->Yaw * MATH_DEG2RAD; + v.y = e->Pitch * MATH_DEG2RAD; return v; } -static Vec3 FirstPersonCamera_GetPosition(float t) { - struct Entity* p = &LocalPlayer_Instance.Base; - Vec3 camPos = Entity_GetEyePosition(p); - float yaw = p->Yaw * MATH_DEG2RAD; - PerspectiveCamera_CalcViewBobbing(t, 1); +static Vec3 FirstPersonCamera_GetPosition(struct LocalPlayer* p, float t) { + struct Entity* e = &p->Base; + Vec3 camPos = Entity_GetEyePosition(e); + float yaw = e->Yaw * MATH_DEG2RAD; + PerspectiveCamera_CalcViewBobbing(p, t, 1); camPos.y += Camera.BobbingVer; - camPos.x += Camera.BobbingHor * (float)Math_Cos(yaw); - camPos.z += Camera.BobbingHor * (float)Math_Sin(yaw); + camPos.x += Camera.BobbingHor * Math_CosF(yaw); + camPos.z += Camera.BobbingHor * Math_SinF(yaw); return camPos; } @@ -185,11 +183,11 @@ static struct Camera cam_FirstPerson = { #define DEF_ZOOM 3.0f static float dist_third = DEF_ZOOM, dist_forward = DEF_ZOOM; -static Vec2 ThirdPersonCamera_GetOrientation(void) { - struct Entity* p = &LocalPlayer_Instance.Base; +static Vec2 ThirdPersonCamera_GetOrientation(struct LocalPlayer* p) { + struct Entity* e = &p->Base; Vec2 v; - v.x = p->Yaw * MATH_DEG2RAD; - v.y = p->Pitch * MATH_DEG2RAD; + v.x = e->Yaw * MATH_DEG2RAD; + v.y = e->Pitch * MATH_DEG2RAD; if (cam_isForwardThird) { v.x += MATH_PI; v.y = -v.y; } v.x += cam_rotOffset.x * MATH_DEG2RAD; @@ -197,29 +195,29 @@ static Vec2 ThirdPersonCamera_GetOrientation(void) { return v; } -static float ThirdPersonCamera_GetZoom(void) { +static float ThirdPersonCamera_GetZoom(struct LocalPlayer* p) { float dist = cam_isForwardThird ? dist_forward : dist_third; /* Don't allow zooming out when -fly */ - if (dist > DEF_ZOOM && !LocalPlayer_CheckCanZoom()) dist = DEF_ZOOM; + if (dist > DEF_ZOOM && !LocalPlayer_CheckCanZoom(p)) dist = DEF_ZOOM; return dist; } -static Vec3 ThirdPersonCamera_GetPosition(float t) { - struct Entity* p = &LocalPlayer_Instance.Base; - float dist = ThirdPersonCamera_GetZoom(); +static Vec3 ThirdPersonCamera_GetPosition(struct LocalPlayer* p, float t) { + struct Entity* e = &p->Base; + float dist = ThirdPersonCamera_GetZoom(p); Vec3 target, dir; Vec2 rot; - PerspectiveCamera_CalcViewBobbing(t, dist); - target = Entity_GetEyePosition(p); + PerspectiveCamera_CalcViewBobbing(p, t, dist); + target = Entity_GetEyePosition(e); target.y += Camera.BobbingVer; - rot = Camera.Active->GetOrientation(); + rot = Camera.Active->GetOrientation(p); dir = Vec3_GetDirVector(rot.x, rot.y); Vec3_Negate(&dir, &dir); Picking_ClipCameraPos(&target, &dir, dist, &cameraClipPos); - return cameraClipPos.Intersect; + return cameraClipPos.intersect; } static cc_bool ThirdPersonCamera_Zoom(float amount) { @@ -255,14 +253,21 @@ static void OnRawMovement(void* obj, float deltaX, float deltaY) { Camera.Active->OnRawMovement(deltaX, deltaY); } +static void OnAxisUpdate(void* obj, int port, int axis, float x, float y) { + if (!Input.RawMode) return; + if (Gamepad_AxisBehaviour[axis] != AXIS_BEHAVIOUR_CAMERA) return; + + Camera.Active->OnRawMovement(x, y); +} + static void OnHacksChanged(void* obj) { - struct HacksComp* h = &LocalPlayer_Instance.Hacks; + struct HacksComp* h = &Entities.CurPlayer->Hacks; /* Leave third person if not allowed anymore */ if (!h->CanUseThirdPerson || !h->Enabled) Camera_CycleActive(); } void Camera_CycleActive(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; + struct LocalPlayer* p = &LocalPlayer_Instances[0]; if (Game_ClassicMode) return; Camera.Active = Camera.Active->next; @@ -316,7 +321,7 @@ static void OnInit(void) { Camera.Active = &cam_FirstPerson; Event_Register_(&PointerEvents.RawMoved, NULL, OnRawMovement); - Event_Register_(&ControllerEvents.RawMoved, NULL, OnRawMovement); + Event_Register_(&ControllerEvents.AxisUpdate, NULL, OnAxisUpdate); Event_Register_(&UserEvents.HackPermsChanged, NULL, OnHacksChanged); #ifdef CC_BUILD_WIN diff --git a/src/Camera.h b/src/Camera.h index 2358b8226..8d8f513d9 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -8,6 +8,7 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 struct RayTracer; struct Camera; struct IGameComponent; +struct LocalPlayer; extern struct IGameComponent Camera_Component; /* Shared data for cameras. */ @@ -45,16 +46,16 @@ struct Camera { /* Calculates the current projection matrix of this camera. */ void (*GetProjection)(struct Matrix* proj); /* Calculates the current modelview matrix of this camera. */ - void (*GetView)(struct Matrix* view); + void (*GetView)(struct LocalPlayer* p, struct Matrix* view); /* Returns the current orientation of the camera. */ - Vec2 (*GetOrientation)(void); + Vec2 (*GetOrientation)(struct LocalPlayer* p); /* Returns the current interpolated position of the camera. */ - Vec3 (*GetPosition)(float t); + Vec3 (*GetPosition)(struct LocalPlayer* p, float t); /* Called to update the camera's state. */ /* Typically, this is used to adjust yaw/pitch based on accumulated mouse movement. */ - void (*UpdateMouse)(double delta); + void (*UpdateMouse)(struct LocalPlayer* p, float delta); /* Called when mouse/pointer has moved. */ void (*OnRawMovement)(float deltaX, float deltaY); /* Called when user closes all menus, and is interacting with camera again. */ @@ -63,7 +64,7 @@ struct Camera { void (*LoseFocus)(void); /* Calculates selected block in the world, based on camera's current state */ - void (*GetPickedBlock)(struct RayTracer* t); + void (*GetPickedBlock)(struct LocalPlayer* p, struct RayTracer* t); /* Zooms the camera in or out when scrolling mouse wheel. */ cc_bool (*Zoom)(float amount); @@ -80,5 +81,5 @@ CC_API void Camera_Register(struct Camera* camera); void Camera_CheckFocus(void); void Camera_UpdateProjection(void); void Camera_SetFov(int fov); -void Camera_KeyLookUpdate(double delta); +void Camera_KeyLookUpdate(float delta); #endif diff --git a/src/Chat.c b/src/Chat.c index 46eaeff2b..c604ff45d 100644 --- a/src/Chat.c +++ b/src/Chat.c @@ -61,7 +61,7 @@ static void ResetLogFile(void) { /* Closes handle to the chat log file */ static void CloseLogFile(void) { cc_result res; - if (!logStream.Meta.File) return; + if (!logStream.meta.file) return; res = logStream.Close(&logStream); if (res) { Logger_SysWarn2(res, "closing", &logPath); } @@ -164,7 +164,7 @@ static void AppendChatLog(const cc_string* text) { } lastLogDay = now.day; lastLogMonth = now.month; lastLogYear = now.year; - if (!logStream.Meta.File) return; + if (!logStream.meta.file) return; /* [HH:mm:ss] text */ String_InitArray(str, strBuffer); diff --git a/src/Chat.h b/src/Chat.h index a97d46334..c4366bfef 100644 --- a/src/Chat.h +++ b/src/Chat.h @@ -67,8 +67,8 @@ typedef void (*FP_Chat_AddOf)(const cc_string* text, int msgType); /* Shorthand for Chat_AddOf(String_FromReadonly(raw), MSG_TYPE_NORMAL) */ void Chat_AddRaw(const char* raw); -void Chat_Add1(const char* format, const void* a1); -void Chat_Add2(const char* format, const void* a1, const void* a2); -void Chat_Add3(const char* format, const void* a1, const void* a2, const void* a3); -void Chat_Add4(const char* format, const void* a1, const void* a2, const void* a3, const void* a4); +CC_API void Chat_Add1(const char* format, const void* a1); +CC_API void Chat_Add2(const char* format, const void* a1, const void* a2); +CC_API void Chat_Add3(const char* format, const void* a1, const void* a2, const void* a3); +CC_API void Chat_Add4(const char* format, const void* a1, const void* a2, const void* a3, const void* a4); #endif diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj index 39539976a..8017c6dab 100644 --- a/src/ClassiCube.vcxproj +++ b/src/ClassiCube.vcxproj @@ -435,6 +435,7 @@ + @@ -458,6 +459,7 @@ + @@ -474,11 +476,14 @@ + + + @@ -500,12 +505,16 @@ + + + + @@ -522,7 +531,7 @@ - + @@ -539,16 +548,20 @@ - + + + + + diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters index 86b40ac23..c694ffe91 100644 --- a/src/ClassiCube.vcxproj.filters +++ b/src/ClassiCube.vcxproj.filters @@ -348,6 +348,9 @@ Header Files\Game + + Source Files\Window + @@ -395,7 +398,7 @@ Source Files\MeshBuilder - + Source Files @@ -620,9 +623,6 @@ Source Files\Platform - - Source Files\Window - Source Files\Window @@ -713,6 +713,45 @@ Source Files\Graphics + + Source Files\Audio + + + Source Files\Window + + + Source Files\Window + + + Source Files\Window + + + Source Files\Window + + + Source Files\Window + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Platform + + + Source Files\Graphics + + + Source Files\Graphics + + + Source Files\Graphics + diff --git a/src/Commands.c b/src/Commands.c index def113c42..872bd4655 100644 --- a/src/Commands.c +++ b/src/Commands.c @@ -237,7 +237,7 @@ static struct ChatCommand ResolutionCommand = { static void ModelCommand_Execute(const cc_string* args, int argsCount) { if (argsCount) { - Entity_SetModel(&LocalPlayer_Instance.Base, args); + Entity_SetModel(&Entities.CurPlayer->Base, args); } else { Chat_AddRaw("&e/client model: &cYou didn't specify a model name."); } @@ -267,6 +267,23 @@ static struct ChatCommand ClearDeniedCommand = { } }; +static void MotdCommand_Execute(const cc_string* args, int argsCount) { + if (Server.IsSinglePlayer) { + Chat_AddRaw("&eThis command can only be used in multiplayer."); + return; + } + Chat_Add1("&eName: &f%s", &Server.Name); + Chat_Add1("&eMOTD: &f%s", &Server.MOTD); +} + +static struct ChatCommand MotdCommand = { + "Motd", MotdCommand_Execute, + COMMAND_FLAG_UNSPLIT_ARGS, + { + "&a/client motd", + "&eDisplays the server's name and MOTD." + } +}; /*########################################################################################################################* *-------------------------------------------------------DrawOpCommand-----------------------------------------------------* @@ -482,7 +499,7 @@ static struct ChatCommand ReplaceCommand = { *------------------------------------------------------TeleportCommand----------------------------------------------------* *#########################################################################################################################*/ static void TeleportCommand_Execute(const cc_string* args, int argsCount) { - struct Entity* e = &LocalPlayer_Instance.Base; + struct Entity* e = &Entities.CurPlayer->Base; struct LocationUpdate update; Vec3 v; @@ -709,6 +726,7 @@ static void OnInit(void) { Commands_Register(&ModelCommand); Commands_Register(&TeleportCommand); Commands_Register(&ClearDeniedCommand); + Commands_Register(&MotdCommand); Commands_Register(&BlockEditCommand); Commands_Register(&CuboidCommand); Commands_Register(&ReplaceCommand); diff --git a/src/Core.h b/src/Core.h index cc0a5bdb7..41da13995 100644 --- a/src/Core.h +++ b/src/Core.h @@ -6,74 +6,74 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 */ #if _MSC_VER -typedef signed __int8 cc_int8; -typedef signed __int16 cc_int16; -typedef signed __int32 cc_int32; -typedef signed __int64 cc_int64; - -typedef unsigned __int8 cc_uint8; -typedef unsigned __int16 cc_uint16; -typedef unsigned __int32 cc_uint32; -typedef unsigned __int64 cc_uint64; -#ifdef _WIN64 -typedef unsigned __int64 cc_uintptr; -#else -typedef unsigned __int32 cc_uintptr; -#endif - -#define CC_INLINE inline -#define CC_NOINLINE __declspec(noinline) -#ifndef CC_API -#define CC_API __declspec(dllexport, noinline) -#define CC_VAR __declspec(dllexport) -#endif - -#define CC_HAS_TYPES -#define CC_HAS_MISC + typedef signed __int8 cc_int8; + typedef signed __int16 cc_int16; + typedef signed __int32 cc_int32; + typedef signed __int64 cc_int64; + + typedef unsigned __int8 cc_uint8; + typedef unsigned __int16 cc_uint16; + typedef unsigned __int32 cc_uint32; + typedef unsigned __int64 cc_uint64; + #ifdef _WIN64 + typedef unsigned __int64 cc_uintptr; + #else + typedef unsigned __int32 cc_uintptr; + #endif + + #define CC_INLINE inline + #define CC_NOINLINE __declspec(noinline) + #ifndef CC_API + #define CC_API __declspec(dllexport, noinline) + #define CC_VAR __declspec(dllexport) + #endif + + #define CC_HAS_TYPES + #define CC_HAS_MISC #elif __GNUC__ -/* really old GCC/clang might not have these defined */ -#ifdef __INT8_TYPE__ -/* avoid including because it breaks defining UNICODE in Platform.c with MinGW */ -typedef __INT8_TYPE__ cc_int8; -typedef __INT16_TYPE__ cc_int16; -typedef __INT32_TYPE__ cc_int32; -typedef __INT64_TYPE__ cc_int64; - -#ifdef __UINT8_TYPE__ -typedef __UINT8_TYPE__ cc_uint8; -typedef __UINT16_TYPE__ cc_uint16; -typedef __UINT32_TYPE__ cc_uint32; -typedef __UINT64_TYPE__ cc_uint64; -typedef __UINTPTR_TYPE__ cc_uintptr; -#else -/* clang doesn't define the __UINT8_TYPE__ */ -typedef unsigned __INT8_TYPE__ cc_uint8; -typedef unsigned __INT16_TYPE__ cc_uint16; -typedef unsigned __INT32_TYPE__ cc_uint32; -typedef unsigned __INT64_TYPE__ cc_uint64; -typedef unsigned __INTPTR_TYPE__ cc_uintptr; -#endif -#define CC_HAS_TYPES -#endif - -#define CC_INLINE inline -#define CC_NOINLINE __attribute__((noinline)) -#ifndef CC_API -#ifdef _WIN32 -#define CC_API __attribute__((dllexport, noinline)) -#define CC_VAR __attribute__((dllexport)) -#else -#define CC_API __attribute__((visibility("default"), noinline)) -#define CC_VAR __attribute__((visibility("default"))) -#endif -#endif -#define CC_HAS_MISC -#ifdef __BIG_ENDIAN__ -#define CC_BIG_ENDIAN -#endif + /* really old GCC/clang might not have these defined */ + #ifdef __INT8_TYPE__ + /* avoid including because it breaks defining UNICODE in Platform.c with MinGW */ + typedef __INT8_TYPE__ cc_int8; + typedef __INT16_TYPE__ cc_int16; + typedef __INT32_TYPE__ cc_int32; + typedef __INT64_TYPE__ cc_int64; + + #ifdef __UINT8_TYPE__ + typedef __UINT8_TYPE__ cc_uint8; + typedef __UINT16_TYPE__ cc_uint16; + typedef __UINT32_TYPE__ cc_uint32; + typedef __UINT64_TYPE__ cc_uint64; + typedef __UINTPTR_TYPE__ cc_uintptr; + #else + /* clang doesn't define the __UINT8_TYPE__ */ + typedef unsigned __INT8_TYPE__ cc_uint8; + typedef unsigned __INT16_TYPE__ cc_uint16; + typedef unsigned __INT32_TYPE__ cc_uint32; + typedef unsigned __INT64_TYPE__ cc_uint64; + typedef unsigned __INTPTR_TYPE__ cc_uintptr; + #endif + #define CC_HAS_TYPES + #endif + + #define CC_INLINE inline + #define CC_NOINLINE __attribute__((noinline)) + #ifndef CC_API + #ifdef _WIN32 + #define CC_API __attribute__((dllexport, noinline)) + #define CC_VAR __attribute__((dllexport)) + #else + #define CC_API __attribute__((visibility("default"), noinline)) + #define CC_VAR __attribute__((visibility("default"))) + #endif + #endif + #define CC_HAS_MISC + #ifdef __BIG_ENDIAN__ + #define CC_BIG_ENDIAN + #endif #elif __MWERKS__ -/* TODO: Is there actual attribute support for CC_API etc somewhere? */ -#define CC_BIG_ENDIAN + /* TODO: Is there actual attribute support for CC_API etc somewhere? */ + #define CC_BIG_ENDIAN #endif /* Unrecognised compiler, so just go with some sensible default typdefs */ @@ -118,32 +118,33 @@ typedef cc_uint8 cc_bool; #endif +#define CC_BUILD_NETWORKING #define CC_BUILD_FREETYPE -#ifndef CC_BUILD_FLATPAK #define CC_BUILD_RESOURCES -#endif +#define CC_BUILD_PLUGINS +#define CC_BUILD_ANIMATIONS +#define CC_BUILD_FILESYSTEM /*#define CC_BUILD_GL11*/ #ifndef CC_BUILD_MANUAL #if defined NXDK /* XBox also defines _WIN32 */ #define CC_BUILD_XBOX + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_NOMUSIC #define CC_BUILD_NOSOUNDS #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_SPLITSCREEN #elif defined XENON /* libxenon also defines __linux__ (yes, really) */ #define CC_BUILD_XBOX360 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_NOMUSIC #define CC_BUILD_NOSOUNDS #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined _WIN32 #define CC_BUILD_WIN #define CC_BUILD_D3D9 @@ -192,12 +193,8 @@ typedef cc_uint8 cc_bool; #define CC_BUILD_IOS #define CC_BUILD_TOUCH #define CC_BUILD_CFNETWORK - #elif defined __x86_64__ || defined __arm64__ - #define CC_BUILD_COCOA - #define CC_BUILD_MACOS - #define CC_BUILD_CURL #else - #define CC_BUILD_CARBON + #define CC_BUILD_COCOA #define CC_BUILD_MACOS #define CC_BUILD_CURL #endif @@ -272,88 +269,139 @@ typedef cc_uint8 cc_bool; #define CC_BUILD_COOPTHREADED #undef CC_BUILD_FREETYPE #undef CC_BUILD_RESOURCES + #undef CC_BUILD_PLUGINS #elif defined __psp__ #define CC_BUILD_PSP + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined __3DS__ #define CC_BUILD_3DS + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE #define CC_BUILD_TOUCH #define CC_BUILD_DUALSCREEN - #undef CC_BUILD_FREETYPE #elif defined GEKKO #define CC_BUILD_GCWII - #define CC_BUILD_OPENAL + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_SPLITSCREEN #elif defined __vita__ #define CC_BUILD_PSVITA + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_TOUCH #elif defined _arch_dreamcast #define CC_BUILD_DREAMCAST - #define CC_BUILD_OPENAL + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_SPLITSCREEN #undef CC_BUILD_RESOURCES #elif defined PLAT_PS3 #define CC_BUILD_PS3 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_LOWMEM #define CC_BUILD_BEARSSL - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_SPLITSCREEN #elif defined N64 #define CC_BIG_ENDIAN #define CC_BUILD_N64 - #define CC_BUILD_OPENAL - #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED + #define CC_BUILD_NOMUSIC + #define CC_BUILD_NOSOUNDS #undef CC_BUILD_RESOURCES + #undef CC_BUILD_NETWORKING + #undef CC_BUILD_FILESYSTEM #elif defined PLAT_PS2 #define CC_BUILD_PS2 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined PLAT_NDS #define CC_BUILD_NDS + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED + #define CC_BUILD_NOMUSIC + #define CC_BUILD_NOSOUNDS + #define CC_BUILD_HTTPCLIENT + #define CC_BUILD_TOUCH + #undef CC_BUILD_RESOURCES + #undef CC_BUILD_ANIMATIONS /* Very costly in FPU less system */ +#elif defined __WIIU__ + #define CC_BUILD_WIIU + #define CC_BUILD_CONSOLE + #define CC_BUILD_COOPTHREADED #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM + #define CC_BUILD_BEARSSL + #define CC_BUILD_SPLITSCREEN + #define CC_BUILD_TOUCH +#elif defined __SWITCH__ + #define CC_BUILD_SWITCH #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_HTTPCLIENT + #define CC_BUILD_BEARSSL + #define CC_BUILD_TOUCH + #define CC_BUILD_GL + #define CC_BUILD_GLMODERN + #define CC_BUILD_GLES + #define CC_BUILD_EGL +#elif defined PLAT_PS1 + #define CC_BUILD_PS1 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED + #define CC_BUILD_NOMUSIC + #define CC_BUILD_NOSOUNDS #undef CC_BUILD_RESOURCES + #undef CC_BUILD_NETWORKING + #undef CC_BUILD_ANIMATIONS /* Very costly in FPU less system */ + #undef CC_BUILD_FILESYSTEM +#elif defined OS2 + #define CC_BUILD_OS2 + #define CC_BUILD_POSIX + #define CC_BUILD_SOFTGPU + #define CC_BUILD_SDL2 + #define CC_BUILD_CURL + #define CC_BUILD_FREETYPE +#elif defined PLAT_SATURN + #define CC_BUILD_SATURN + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED + #define CC_BUILD_NOMUSIC + #define CC_BUILD_NOSOUNDS + #undef CC_BUILD_RESOURCES + #undef CC_BUILD_NETWORKING + #undef CC_BUILD_ANIMATIONS /* Very costly in FPU less system */ + #undef CC_BUILD_FILESYSTEM #endif #endif +#ifdef CC_BUILD_CONSOLE +#undef CC_BUILD_FREETYPE +#undef CC_BUILD_PLUGINS +#endif #ifndef CC_BUILD_LOWMEM #define EXTENDED_BLOCKS @@ -379,8 +427,8 @@ typedef cc_uint8 Face; typedef cc_uint32 cc_result; typedef cc_uint64 TimeMS; -typedef struct Rect2D_ { int x, y, Width, Height; } Rect2D; -typedef struct TextureRec_ { float U1, V1, U2, V2; } TextureRec; +typedef struct Rect2D_ { int x, y, width, height; } Rect2D; +typedef struct TextureRec_ { float u1, v1, u2, v2; } TextureRec; typedef struct cc_string_ { char* buffer; /* Pointer to characters, NOT NULL TERMINATED */ @@ -402,7 +450,7 @@ typedef void* GfxResourceID; /* Contains the information to describe a 2D textured quad. */ struct Texture { GfxResourceID ID; - short x, y; cc_uint16 Width, Height; + short x, y; cc_uint16 width, height; TextureRec uv; }; #endif diff --git a/src/Deflate.c b/src/Deflate.c index 0e8970501..747288616 100644 --- a/src/Deflate.c +++ b/src/Deflate.c @@ -182,9 +182,9 @@ static cc_result Huffman_Build(struct HuffmanTable* table, const cc_uint8* bitLe int i, j; /* Initialise 'zero bit length' codewords */ - table->FirstCodewords[0] = 0; - table->FirstOffsets[0] = 0; - table->EndCodewords[0] = 0; + table->firstCodewords[0] = 0; + table->firstOffsets[0] = 0; + table->endCodewords[0] = 0; /* Count number of codewords assigned to each bit length */ for (i = 0; i < INFLATE_MAX_BITS; i++) bl_count[i] = 0; @@ -209,8 +209,8 @@ static cc_result Huffman_Build(struct HuffmanTable* table, const cc_uint8* bitLe code = (code + bl_count[i - 1]) << 1; bl_offsets[i] = offset; - table->FirstCodewords[i] = code; - table->FirstOffsets[i] = offset; + table->firstCodewords[i] = code; + table->firstOffsets[i] = offset; offset += bl_count[i]; /* Last codeword is actually: code + (bl_count[i] - 1) @@ -218,9 +218,9 @@ static cc_result Huffman_Build(struct HuffmanTable* table, const cc_uint8* bitLe * This way, don't need to special case bit lengths with 0 codewords when decoding. */ if (bl_count[i]) { - table->EndCodewords[i] = code + bl_count[i]; + table->endCodewords[i] = code + bl_count[i]; } else { - table->EndCodewords[i] = 0; + table->endCodewords[i] = 0; } } @@ -229,11 +229,11 @@ static cc_result Huffman_Build(struct HuffmanTable* table, const cc_uint8* bitLe * Some values may also not be assigned to any codeword. */ value = 0; - Mem_Set(table->Fast, UInt8_MaxValue, sizeof(table->Fast)); + Mem_Set(table->fast, UInt8_MaxValue, sizeof(table->fast)); for (i = 0; i < count; i++, value++) { int len = bitLens[i]; if (!len) continue; - table->Values[bl_offsets[len]] = value; + table->values[bl_offsets[len]] = value; /* Compute the accelerated lookup table values for this codeword. * For example, assume len = 4 and codeword = 0100 @@ -244,12 +244,12 @@ static cc_result Huffman_Build(struct HuffmanTable* table, const cc_uint8* bitLe */ if (len <= INFLATE_FAST_BITS) { cc_int16 packed = (cc_int16)((len << INFLATE_FAST_BITS) | value); - int codeword = table->FirstCodewords[len] + (bl_offsets[len] - table->FirstOffsets[len]); + int codeword = table->firstCodewords[len] + (bl_offsets[len] - table->firstOffsets[len]); codeword <<= (INFLATE_FAST_BITS - len); for (j = 0; j < 1 << (INFLATE_FAST_BITS - len); j++, codeword++) { int index = Huffman_ReverseBits(codeword, INFLATE_FAST_BITS); - table->Fast[index] = packed; + table->fast[index] = packed; } } bl_offsets[len]++; @@ -271,7 +271,7 @@ static int Huffman_Decode(struct InflateState* state, struct HuffmanTable* table /* Try fast accelerated table lookup */ if (state->NumBits >= INFLATE_FAST_BITS) { - packed = table->Fast[Inflate_PeekBits(state, INFLATE_FAST_BITS)]; + packed = table->fast[Inflate_PeekBits(state, INFLATE_FAST_BITS)]; if (packed >= 0) { bits = packed >> INFLATE_FAST_BITS; Inflate_ConsumeBits(state, bits); @@ -285,10 +285,10 @@ static int Huffman_Decode(struct InflateState* state, struct HuffmanTable* table if (state->NumBits < i) return -1; codeword = (codeword << 1) | ((state->Bits >> j) & 1); - if (codeword < table->EndCodewords[i]) { - offset = table->FirstOffsets[i] + (codeword - table->FirstCodewords[i]); + if (codeword < table->endCodewords[i]) { + offset = table->firstOffsets[i] + (codeword - table->firstCodewords[i]); Inflate_ConsumeBits(state, i); - return table->Values[offset]; + return table->values[offset]; } } @@ -300,7 +300,7 @@ static int Huffman_Decode(struct InflateState* state, struct HuffmanTable* table #define Huffman_UNSAFE_Decode(state, table, result) \ {\ Inflate_UNSAFE_EnsureBits(state, INFLATE_MAX_BITS);\ - packed = table.Fast[Inflate_PeekBits(state, INFLATE_FAST_BITS)];\ + packed = table.fast[Inflate_PeekBits(state, INFLATE_FAST_BITS)];\ if (packed >= 0) {\ consumedBits = packed >> INFLATE_FAST_BITS;\ Inflate_ConsumeBits(state, consumedBits);\ @@ -321,10 +321,10 @@ static int Huffman_UNSAFE_Decode_Slow(struct InflateState* state, struct Huffman for (i = INFLATE_FAST_BITS + 1, j = INFLATE_FAST_BITS; i < INFLATE_MAX_BITS; i++, j++) { codeword = (codeword << 1) | ((state->Bits >> j) & 1); - if (codeword < table->EndCodewords[i]) { - offset = table->FirstOffsets[i] + (codeword - table->FirstCodewords[i]); + if (codeword < table->endCodewords[i]) { + offset = table->firstOffsets[i] + (codeword - table->firstCodewords[i]); Inflate_ConsumeBits(state, i); - return table->Values[offset]; + return table->values[offset]; } } @@ -730,7 +730,7 @@ static cc_result Inflate_StreamRead(struct Stream* stream, cc_uint8* data, cc_ui cc_result res; *modified = 0; - state = (struct InflateState*)stream->Meta.Inflate; + state = (struct InflateState*)stream->meta.inflate; state->Output = data; state->AvailOut = count; @@ -764,7 +764,7 @@ static cc_result Inflate_StreamRead(struct Stream* stream, cc_uint8* data, cc_ui void Inflate_MakeStream2(struct Stream* stream, struct InflateState* state, struct Stream* underlying) { Stream_Init(stream); Inflate_Init2(state, underlying); - stream->Meta.Inflate = state; + stream->meta.inflate = state; stream->Read = Inflate_StreamRead; } @@ -941,7 +941,7 @@ static cc_result Deflate_StreamWrite(struct Stream* stream, const cc_uint8* data struct DeflateState* state; cc_result res; - state = (struct DeflateState*)stream->Meta.Inflate; + state = (struct DeflateState*)stream->meta.inflate; *modified = 0; while (total > 0) { @@ -970,7 +970,7 @@ static cc_result Deflate_StreamClose(struct Stream* stream) { struct DeflateState* state; cc_result res; - state = (struct DeflateState*)stream->Meta.Inflate; + state = (struct DeflateState*)stream->meta.inflate; res = Deflate_FlushBlock(state, state->InputPosition - DEFLATE_BLOCK_SIZE); if (res) return res; @@ -995,12 +995,12 @@ static void Deflate_BuildTable(const cc_uint8* lens, int count, cc_uint16* codew /* NOTE: Can ignore since lens table is not user controlled */ (void)Huffman_Build(&table, lens, count); for (i = 0; i < INFLATE_MAX_BITS; i++) { - if (!table.EndCodewords[i]) continue; - count = table.EndCodewords[i] - table.FirstCodewords[i]; + if (!table.endCodewords[i]) continue; + count = table.endCodewords[i] - table.firstCodewords[i]; for (j = 0; j < count; j++) { - offset = table.Values[table.FirstOffsets[i] + j]; - codeword = table.FirstCodewords[i] + j; + offset = table.values[table.firstOffsets[i] + j]; + codeword = table.firstCodewords[i] + j; bitlens[offset] = i; codewords[offset] = Huffman_ReverseBits(codeword, i); } @@ -1009,7 +1009,7 @@ static void Deflate_BuildTable(const cc_uint8* lens, int count, cc_uint16* codew void Deflate_MakeStream(struct Stream* stream, struct DeflateState* state, struct Stream* underlying) { Stream_Init(stream); - stream->Meta.Inflate = state; + stream->meta.inflate = state; stream->Write = Deflate_StreamWrite; stream->Close = Deflate_StreamClose; @@ -1033,7 +1033,7 @@ void Deflate_MakeStream(struct Stream* stream, struct DeflateState* state, struc *-----------------------------------------------------GZip (compress)-----------------------------------------------------* *#########################################################################################################################*/ static cc_result GZip_StreamClose(struct Stream* stream) { - struct GZipState* state = (struct GZipState*)stream->Meta.Inflate; + struct GZipState* state = (struct GZipState*)stream->meta.inflate; cc_uint8 data[8]; cc_result res; @@ -1044,7 +1044,7 @@ static cc_result GZip_StreamClose(struct Stream* stream) { } static cc_result GZip_StreamWrite(struct Stream* stream, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { - struct GZipState* state = (struct GZipState*)stream->Meta.Inflate; + struct GZipState* state = (struct GZipState*)stream->meta.inflate; cc_uint32 i, crc32 = state->Crc32; state->Size += count; @@ -1059,7 +1059,7 @@ static cc_result GZip_StreamWrite(struct Stream* stream, const cc_uint8* data, c static cc_result GZip_StreamWriteFirst(struct Stream* stream, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { static cc_uint8 header[10] = { 0x1F, 0x8B, 0x08 }; /* GZip header */ - struct GZipState* state = (struct GZipState*)stream->Meta.Inflate; + struct GZipState* state = (struct GZipState*)stream->meta.inflate; cc_result res; if ((res = Stream_Write(state->Base.Dest, header, sizeof(header)))) return res; @@ -1080,7 +1080,7 @@ void GZip_MakeStream(struct Stream* stream, struct GZipState* state, struct Stre *-----------------------------------------------------ZLib (compress)-----------------------------------------------------* *#########################################################################################################################*/ static cc_result ZLib_StreamClose(struct Stream* stream) { - struct ZLibState* state = (struct ZLibState*)stream->Meta.Inflate; + struct ZLibState* state = (struct ZLibState*)stream->meta.inflate; cc_uint8 data[4]; cc_result res; @@ -1090,7 +1090,7 @@ static cc_result ZLib_StreamClose(struct Stream* stream) { } static cc_result ZLib_StreamWrite(struct Stream* stream, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { - struct ZLibState* state = (struct ZLibState*)stream->Meta.Inflate; + struct ZLibState* state = (struct ZLibState*)stream->meta.inflate; cc_uint32 i, adler32 = state->Adler32; cc_uint32 s1 = adler32 & 0xFFFF, s2 = (adler32 >> 16) & 0xFFFF; @@ -1107,7 +1107,7 @@ static cc_result ZLib_StreamWrite(struct Stream* stream, const cc_uint8* data, c static cc_result ZLib_StreamWriteFirst(struct Stream* stream, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { static cc_uint8 header[2] = { 0x78, 0x9C }; /* ZLib header */ - struct ZLibState* state = (struct ZLibState*)stream->Meta.Inflate; + struct ZLibState* state = (struct ZLibState*)stream->meta.inflate; cc_result res; if ((res = Stream_Write(state->Base.Dest, header, sizeof(header)))) return res; @@ -1126,7 +1126,7 @@ void ZLib_MakeStream(struct Stream* stream, struct ZLibState* state, struct Stre /*########################################################################################################################* *--------------------------------------------------------ZipReader--------------------------------------------------------* *#########################################################################################################################*/ -#define ZIP_MAXNAMELEN 512 +#define ZIP_MAXNAMELEN 512 #define ZIP_MAX_ENTRIES 1024 /* Stores state for reading and processing entries in a .zip archive */ @@ -1216,7 +1216,6 @@ static cc_result Zip_ReadCentralDirectory(struct ZipState* state) { if (state->usedEntries >= ZIP_MAX_ENTRIES) return ZIP_ERR_TOO_MANY_ENTRIES; entry = &state->entries[state->usedEntries++]; - entry->CRC32 = Stream_GetU32_LE(&header[12]); entry->CompressedSize = Stream_GetU32_LE(&header[16]); entry->UncompressedSize = Stream_GetU32_LE(&header[20]); entry->LocalHeaderOffset = Stream_GetU32_LE(&header[38]); diff --git a/src/Deflate.h b/src/Deflate.h index 60d92f81b..7e363bdba 100644 --- a/src/Deflate.h +++ b/src/Deflate.h @@ -32,11 +32,11 @@ cc_result ZLibHeader_Read(struct Stream* s, struct ZLibHeader* header); #define INFLATE_WINDOW_MASK 0x7FFFUL struct HuffmanTable { - cc_int16 Fast[1 << INFLATE_FAST_BITS]; /* Fast lookup table for huffman codes */ - cc_uint16 FirstCodewords[INFLATE_MAX_BITS]; /* Starting codeword for each bit length */ - cc_uint16 EndCodewords[INFLATE_MAX_BITS]; /* (Last codeword + 1) for each bit length. 0 is ignored. */ - cc_uint16 FirstOffsets[INFLATE_MAX_BITS]; /* Base offset into Values for codewords of each bit length. */ - cc_uint16 Values[INFLATE_MAX_LITS]; /* Values/Symbols list */ + cc_int16 fast[1 << INFLATE_FAST_BITS]; /* Fast lookup table for huffman codes */ + cc_uint16 firstCodewords[INFLATE_MAX_BITS]; /* Starting codeword for each bit length */ + cc_uint16 endCodewords[INFLATE_MAX_BITS]; /* (Last codeword + 1) for each bit length. 0 is ignored. */ + cc_uint16 firstOffsets[INFLATE_MAX_BITS]; /* Base offset into Values for codewords of each bit length. */ + cc_uint16 values[INFLATE_MAX_LITS]; /* Values/Symbols list */ }; struct InflateState { @@ -120,7 +120,7 @@ CC_API void ZLib_MakeStream( struct Stream* stream, struct ZLibState* stat typedef void (*FP_ZLib_MakeStream)(struct Stream* stream, struct ZLibState* state, struct Stream* underlying); /* Minimal data needed to describe an entry in a .zip archive */ -struct ZipEntry { cc_uint32 CompressedSize, UncompressedSize, LocalHeaderOffset, CRC32; }; +struct ZipEntry { cc_uint32 CompressedSize, UncompressedSize, LocalHeaderOffset; }; /* Callback function to process the data in a .zip archive entry */ /* Return non-zero to indicate an error and stop further processing */ /* NOTE: data stream MAY NOT be seekable (i.e. entry data might be compressed) */ diff --git a/src/Drawer.c b/src/Drawer.c index 60eb57a39..ec80e1dbd 100644 --- a/src/Drawer.c +++ b/src/Drawer.c @@ -5,7 +5,7 @@ struct _DrawerData Drawer; void Drawer_XMin(int count, PackedCol col, TextureLoc texLoc, struct VertexTextured** vertices) { - struct VertexTextured* ptr = *vertices; struct VertexTextured v; + struct VertexTextured* v = *vertices; float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize; float u1 = Drawer.MinBB.z; @@ -13,18 +13,21 @@ void Drawer_XMin(int count, PackedCol col, TextureLoc texLoc, struct VertexTextu float v1 = vOrigin + Drawer.MaxBB.y * Atlas1D.InvTileSize; float v2 = vOrigin + Drawer.MinBB.y * Atlas1D.InvTileSize * UV2_Scale; - if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); - v.x = Drawer.X1; v.Col = col; + float x1 = Drawer.X1; + float y1 = Drawer.Y1, y2 = Drawer.Y2; + float z1 = Drawer.Z1, z2 = Drawer.Z2 + (count - 1); - v.y = Drawer.Y2; v.z = Drawer.Z2 + (count - 1); v.U = u2; v.V = v1; *ptr++ = v; - v.z = Drawer.Z1; v.U = u1; *ptr++ = v; - v.y = Drawer.Y1; v.V = v2; *ptr++ = v; - v.z = Drawer.Z2 + (count - 1); v.U = u2; *ptr++ = v; - *vertices = ptr; + if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); + + v->x = x1; v->y = y2; v->z = z2; v->Col = col; v->U = u2; v->V = v1; v++; + v->x = x1; v->y = y2; v->z = z1; v->Col = col; v->U = u1; v->V = v1; v++; + v->x = x1; v->y = y1; v->z = z1; v->Col = col; v->U = u1; v->V = v2; v++; + v->x = x1; v->y = y1; v->z = z2; v->Col = col; v->U = u2; v->V = v2; v++; + *vertices = v; } void Drawer_XMax(int count, PackedCol col, TextureLoc texLoc, struct VertexTextured** vertices) { - struct VertexTextured* ptr = *vertices; struct VertexTextured v; + struct VertexTextured* v = *vertices; float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize; float u1 = (count - Drawer.MinBB.z); @@ -32,18 +35,21 @@ void Drawer_XMax(int count, PackedCol col, TextureLoc texLoc, struct VertexTextu float v1 = vOrigin + Drawer.MaxBB.y * Atlas1D.InvTileSize; float v2 = vOrigin + Drawer.MinBB.y * Atlas1D.InvTileSize * UV2_Scale; - if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); - v.x = Drawer.X2; v.Col = col; + float x2 = Drawer.X2; + float y1 = Drawer.Y1, y2 = Drawer.Y2; + float z1 = Drawer.Z1, z2 = Drawer.Z2 + (count - 1); - v.y = Drawer.Y2; v.z = Drawer.Z1; v.U = u1; v.V = v1; *ptr++ = v; - v.z = Drawer.Z2 + (count - 1); v.U = u2; *ptr++ = v; - v.y = Drawer.Y1; v.V = v2; *ptr++ = v; - v.z = Drawer.Z1; v.U = u1; *ptr++ = v; - *vertices = ptr; + if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); + + v->x = x2; v->y = y2; v->z = z1; v->Col = col; v->U = u1; v->V = v1; v++; + v->x = x2; v->y = y2; v->z = z2; v->Col = col; v->U = u2; v->V = v1; v++; + v->x = x2; v->y = y1; v->z = z2; v->Col = col; v->U = u2; v->V = v2; v++; + v->x = x2; v->y = y1; v->z = z1; v->Col = col; v->U = u1; v->V = v2; v++; + *vertices = v; } void Drawer_ZMin(int count, PackedCol col, TextureLoc texLoc, struct VertexTextured** vertices) { - struct VertexTextured* ptr = *vertices; struct VertexTextured v; + struct VertexTextured* v = *vertices; float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize; float u1 = (count - Drawer.MinBB.x); @@ -51,18 +57,21 @@ void Drawer_ZMin(int count, PackedCol col, TextureLoc texLoc, struct VertexTextu float v1 = vOrigin + Drawer.MaxBB.y * Atlas1D.InvTileSize; float v2 = vOrigin + Drawer.MinBB.y * Atlas1D.InvTileSize * UV2_Scale; - if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); - v.z = Drawer.Z1; v.Col = col; + float x1 = Drawer.X1, x2 = Drawer.X2 + (count - 1); + float y1 = Drawer.Y1, y2 = Drawer.Y2; + float z1 = Drawer.Z1; - v.x = Drawer.X2 + (count - 1); v.y = Drawer.Y1; v.U = u2; v.V = v2; *ptr++ = v; - v.x = Drawer.X1; v.U = u1; *ptr++ = v; - v.y = Drawer.Y2; v.V = v1; *ptr++ = v; - v.x = Drawer.X2 + (count - 1); v.U = u2; *ptr++ = v; - *vertices = ptr; + if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); + + v->x = x2; v->y = y1; v->z = z1; v->Col = col; v->U = u2; v->V = v2; v++; + v->x = x1; v->y = y1; v->z = z1; v->Col = col; v->U = u1; v->V = v2; v++; + v->x = x1; v->y = y2; v->z = z1; v->Col = col; v->U = u1; v->V = v1; v++; + v->x = x2; v->y = y2; v->z = z1; v->Col = col; v->U = u2; v->V = v1; v++; + *vertices = v; } void Drawer_ZMax(int count, PackedCol col, TextureLoc texLoc, struct VertexTextured** vertices) { - struct VertexTextured* ptr = *vertices; struct VertexTextured v; + struct VertexTextured* v = *vertices; float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize; float u1 = Drawer.MinBB.x; @@ -70,18 +79,21 @@ void Drawer_ZMax(int count, PackedCol col, TextureLoc texLoc, struct VertexTextu float v1 = vOrigin + Drawer.MaxBB.y * Atlas1D.InvTileSize; float v2 = vOrigin + Drawer.MinBB.y * Atlas1D.InvTileSize * UV2_Scale; - if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); - v.z = Drawer.Z2; v.Col = col; + float x1 = Drawer.X1, x2 = Drawer.X2 + (count - 1); + float y1 = Drawer.Y1, y2 = Drawer.Y2; + float z2 = Drawer.Z2; - v.x = Drawer.X2 + (count - 1); v.y = Drawer.Y2; v.U = u2; v.V = v1; *ptr++ = v; - v.x = Drawer.X1; v.U = u1; *ptr++ = v; - v.y = Drawer.Y1; v.V = v2; *ptr++ = v; - v.x = Drawer.X2 + (count - 1); v.U = u2; *ptr++ = v; - *vertices = ptr; + if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); + + v->x = x2; v->y = y2; v->z = z2; v->Col = col; v->U = u2; v->V = v1; v++; + v->x = x1; v->y = y2; v->z = z2; v->Col = col; v->U = u1; v->V = v1; v++; + v->x = x1; v->y = y1; v->z = z2; v->Col = col; v->U = u1; v->V = v2; v++; + v->x = x2; v->y = y1; v->z = z2; v->Col = col; v->U = u2; v->V = v2; v++; + *vertices = v; } void Drawer_YMin(int count, PackedCol col, TextureLoc texLoc, struct VertexTextured** vertices) { - struct VertexTextured* ptr = *vertices; struct VertexTextured v; + struct VertexTextured* v = *vertices; float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize; float u1 = Drawer.MinBB.x; @@ -89,18 +101,21 @@ void Drawer_YMin(int count, PackedCol col, TextureLoc texLoc, struct VertexTextu float v1 = vOrigin + Drawer.MinBB.z * Atlas1D.InvTileSize; float v2 = vOrigin + Drawer.MaxBB.z * Atlas1D.InvTileSize * UV2_Scale; - if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); - v.y = Drawer.Y1; v.Col = col; + float x1 = Drawer.X1, x2 = Drawer.X2 + (count - 1); + float y1 = Drawer.Y1; + float z1 = Drawer.Z1, z2 = Drawer.Z2; - v.x = Drawer.X2 + (count - 1); v.z = Drawer.Z2; v.U = u2; v.V = v2; *ptr++ = v; - v.x = Drawer.X1; v.U = u1; *ptr++ = v; - v.z = Drawer.Z1; v.V = v1; *ptr++ = v; - v.x = Drawer.X2 + (count - 1); v.U = u2; *ptr++ = v; - *vertices = ptr; + if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); + + v->x = x2; v->y = y1; v->z = z2; v->Col = col; v->U = u2; v->V = v2; v++; + v->x = x1; v->y = y1; v->z = z2; v->Col = col; v->U = u1; v->V = v2; v++; + v->x = x1; v->y = y1; v->z = z1; v->Col = col; v->U = u1; v->V = v1; v++; + v->x = x2; v->y = y1; v->z = z1; v->Col = col; v->U = u2; v->V = v1; v++; + *vertices = v; } void Drawer_YMax(int count, PackedCol col, TextureLoc texLoc, struct VertexTextured** vertices) { - struct VertexTextured* ptr = *vertices; struct VertexTextured v; + struct VertexTextured* v = *vertices; float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize; float u1 = Drawer.MinBB.x; @@ -108,12 +123,15 @@ void Drawer_YMax(int count, PackedCol col, TextureLoc texLoc, struct VertexTextu float v1 = vOrigin + Drawer.MinBB.z * Atlas1D.InvTileSize; float v2 = vOrigin + Drawer.MaxBB.z * Atlas1D.InvTileSize * UV2_Scale; - if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); - v.y = Drawer.Y2; v.Col = col; + float x1 = Drawer.X1, x2 = Drawer.X2 + (count - 1); + float y2 = Drawer.Y2; + float z1 = Drawer.Z1, z2 = Drawer.Z2; - v.x = Drawer.X2 + (count - 1); v.z = Drawer.Z1; v.U = u2; v.V = v1; *ptr++ = v; - v.x = Drawer.X1; v.U = u1; *ptr++ = v; - v.z = Drawer.Z2; v.V = v2; *ptr++ = v; - v.x = Drawer.X2 + (count - 1); v.U = u2; *ptr++ = v; - *vertices = ptr; + if (Drawer.Tinted) col = PackedCol_Tint(col, Drawer.TintCol); + + v->x = x2; v->y = y2; v->z = z1; v->Col = col; v->U = u2; v->V = v1; v++; + v->x = x1; v->y = y2; v->z = z1; v->Col = col; v->U = u1; v->V = v1; v++; + v->x = x1; v->y = y2; v->z = z2; v->Col = col; v->U = u1; v->V = v2; v++; + v->x = x2; v->y = y2; v->z = z2; v->Col = col; v->U = u2; v->V = v2; v++; + *vertices = v; } diff --git a/src/Drawer2D.c b/src/Drawer2D.c index 771bda728..589ca0b24 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -157,6 +157,9 @@ void Context2D_Alloc(struct Context2D* ctx, int width, int height) { width = Math_NextPowOf2(width); height = Math_NextPowOf2(height); } + + if (Gfx.MinTexWidth) { width = max(width, Gfx.MinTexWidth); } + if (Gfx.MinTexHeight) { height = max(height, Gfx.MinTexHeight); } ctx->bmp.width = width; ctx->bmp.height = height; @@ -174,13 +177,17 @@ void Context2D_Free(struct Context2D* ctx) { Mem_Free(ctx->bmp.scan0); } +#define BitmapColor_Raw(r, g, b) (BitmapColor_R_Bits(r) | BitmapColor_G_Bits(g) | BitmapColor_B_Bits(b)) void Gradient_Noise(struct Context2D* ctx, BitmapCol color, int variation, int x, int y, int width, int height) { struct Bitmap* bmp = (struct Bitmap*)ctx; BitmapCol* dst; int R, G, B, xx, yy, n; - float noise; + int noise, delta; + cc_uint32 alpha; + if (!Drawer2D_Clamp(ctx, &x, &y, &width, &height)) return; + alpha = color & BITMAPCOLOR_A_MASK; for (yy = 0; yy < height; yy++) { dst = Bitmap_GetRow(bmp, y + yy) + x; @@ -188,13 +195,20 @@ void Gradient_Noise(struct Context2D* ctx, BitmapCol color, int variation, for (xx = 0; xx < width; xx++, dst++) { n = (x + xx) + (y + yy) * 57; n = (n << 13) ^ n; - noise = 1.0f - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f; - R = BitmapCol_R(color) + (int)(noise * variation); Drawer2D_ClampPixel(R); - G = BitmapCol_G(color) + (int)(noise * variation); Drawer2D_ClampPixel(G); - B = BitmapCol_B(color) + (int)(noise * variation); Drawer2D_ClampPixel(B); + /* + float noise = 1.0f - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f; + int delta = (int)(noise * variation); + */ + /* Fixed point equivalent to the above expression */ + noise = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); + delta = (((1024 - noise / 0x100000)) * variation) >> 10; - *dst = BitmapColor_RGB(R, G, B); + R = BitmapCol_R(color) + delta; Drawer2D_ClampPixel(R); + G = BitmapCol_G(color) + delta; Drawer2D_ClampPixel(G); + B = BitmapCol_B(color) + delta; Drawer2D_ClampPixel(B); + + *dst = BitmapColor_Raw(R, G, B) | alpha; } } } @@ -304,12 +318,12 @@ void Context2D_MakeTexture(struct Texture* tex, struct Context2D* ctx) { int flags = TEXTURE_FLAG_NONPOW2 | TEXTURE_FLAG_LOWRES; Gfx_RecreateTexture(&tex->ID, &ctx->bmp, flags, false); - tex->Width = ctx->width; - tex->Height = ctx->height; + tex->width = ctx->width; + tex->height = ctx->height; - tex->uv.U1 = 0.0f; tex->uv.V1 = 0.0f; - tex->uv.U2 = (float)ctx->width / (float)ctx->bmp.width; - tex->uv.V2 = (float)ctx->height / (float)ctx->bmp.height; + tex->uv.u1 = 0.0f; tex->uv.v1 = 0.0f; + tex->uv.u2 = (float)ctx->width / (float)ctx->bmp.width; + tex->uv.v2 = (float)ctx->height / (float)ctx->bmp.height; } cc_bool Drawer2D_ValidColorCodeAt(const cc_string* text, int i) { @@ -395,10 +409,10 @@ void Drawer2D_ReducePadding_Tex(struct Texture* tex, int point, int scale) { float vAdj; if (!Drawer2D.BitmappedText) return; - padding = (tex->Height - point) / scale; - vAdj = (float)padding / Math_NextPowOf2(tex->Height); - tex->uv.V1 += vAdj; tex->uv.V2 -= vAdj; - tex->Height -= (cc_uint16)(padding * 2); + padding = (tex->height - point) / scale; + vAdj = (float)padding / Math_NextPowOf2(tex->height); + tex->uv.v1 += vAdj; tex->uv.v2 -= vAdj; + tex->height -= (cc_uint16)(padding * 2); } void Drawer2D_ReducePadding_Height(int* height, int point, int scale) { @@ -525,6 +539,12 @@ static void DrawBitmappedTextCore(struct Bitmap* bmp, struct DrawTextArgs* args, static void DrawBitmappedText(struct Bitmap* bmp, struct DrawTextArgs* args, int x, int y) { int offset = Drawer2D_ShadowOffset(args->font->size); + if (!fontBitmap.scan0) { + if (args->useShadow) FallbackFont_DrawText(args, bmp, x, y, true); + FallbackFont_DrawText(args, bmp, x, y, false); + return; + } + if (args->useShadow) { DrawBitmappedTextCore(bmp, args, x + offset, y + offset, true); } @@ -536,6 +556,8 @@ static int MeasureBitmappedWidth(const struct DrawTextArgs* args) { int xPadding, width; cc_string text; + if (!fontBitmap.scan0) return FallbackFont_TextWidth(args); + /* adjust coords to make drawn text match GDI fonts */ xPadding = Drawer2D_XPadding(point); width = 0; diff --git a/src/Entity.c b/src/Entity.c index ff8f1bdb8..40ebb818e 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -367,7 +367,7 @@ static void Entity_CheckSkin(struct Entity* e) { if (!e->SkinFetchState) { first = Entity_FirstOtherWithSameSkinAndFetchedSkin(e); - flags = e == &LocalPlayer_Instance.Base ? HTTP_FLAG_NOCACHE : 0; + flags = e == &LocalPlayer_Instances[0].Base ? HTTP_FLAG_NOCACHE : 0; if (!first) { e->_skinReqID = Http_AsyncGetSkin(&skin, flags); @@ -452,7 +452,7 @@ void Entities_Tick(struct ScheduledTask* task) { } } -void Entities_RenderModels(double delta, float t) { +void Entities_RenderModels(float delta, float t) { int i; Gfx_SetAlphaTest(true); @@ -497,24 +497,24 @@ void Entities_Remove(EntityID id) { } } -EntityID Entities_GetClosest(struct Entity* src) { +int Entities_GetClosest(struct Entity* src) { Vec3 eyePos = Entity_GetEyePosition(src); Vec3 dir = Vec3_GetDirVector(src->Yaw * MATH_DEG2RAD, src->Pitch * MATH_DEG2RAD); float closestDist = -200; /* NOTE: was previously positive infinity */ - EntityID targetID = ENTITIES_SELF_ID; + int targetID = -1; float t0, t1; int i; - for (i = 0; i < ENTITIES_SELF_ID; i++) /* because we don't want to pick against local player */ + for (i = 0; i < ENTITIES_MAX_COUNT; i++) /* because we don't want to pick against local player */ { - struct Entity* entity = Entities.List[i]; - if (!entity) continue; - if (!Intersection_RayIntersectsRotatedBox(eyePos, dir, entity, &t0, &t1)) continue; + struct Entity* e = Entities.List[i]; + if (!e || e == &Entities.CurPlayer->Base) continue; + if (!Intersection_RayIntersectsRotatedBox(eyePos, dir, e, &t0, &t1)) continue; - if (targetID == ENTITIES_SELF_ID || t0 < closestDist) { + if (targetID == -1 || t0 < closestDist) { closestDist = t0; - targetID = (EntityID)i; + targetID = i; } } return targetID; @@ -613,23 +613,27 @@ struct IGameComponent TabList_Component = { /*########################################################################################################################* *------------------------------------------------------LocalPlayer--------------------------------------------------------* *#########################################################################################################################*/ -struct LocalPlayer LocalPlayer_Instance; +struct LocalPlayer LocalPlayer_Instances[MAX_LOCAL_PLAYERS]; static cc_bool hackPermMsgs; -float LocalPlayer_JumpHeight(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static struct LocalPlayerInput* sources_head; +static struct LocalPlayerInput* sources_tail; + +void LocalPlayerInput_Add(struct LocalPlayerInput* source) { + LinkedList_Append(source, sources_head, sources_tail); +} + +float LocalPlayer_JumpHeight(struct LocalPlayer* p) { return (float)PhysicsComp_CalcMaxHeight(p->Physics.JumpVel); } -void LocalPlayer_SetInterpPosition(float t) { - struct LocalPlayer* p = &LocalPlayer_Instance; +void LocalPlayer_SetInterpPosition(struct LocalPlayer* p, float t) { if (!(p->Hacks.WOMStyleHacks && p->Hacks.Noclip)) { Vec3_Lerp(&p->Base.Position, &p->Base.prev.pos, &p->Base.next.pos, t); } Entity_LerpAngles(&p->Base, t); } -static void LocalPlayer_HandleInput(float* xMoving, float* zMoving) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static void LocalPlayer_HandleInput(struct LocalPlayer* p, float* xMoving, float* zMoving) { struct HacksComp* hacks = &p->Hacks; struct LocalPlayerInput* input; @@ -640,8 +644,8 @@ static void LocalPlayer_HandleInput(float* xMoving, float* zMoving) { } /* keyboard input, touch, joystick, etc */ - for (input = &p->input; input; input = input->next) { - input->GetMovement(xMoving, zMoving); + for (input = sources_head; input; input = input->next) { + input->GetMovement(p, xMoving, zMoving); } *xMoving *= 0.98f; *zMoving *= 0.98f; @@ -660,7 +664,7 @@ static void LocalPlayer_HandleInput(float* xMoving, float* zMoving) { } static void LocalPlayer_InputSet(int key, cc_bool pressed) { - struct HacksComp* hacks = &LocalPlayer_Instance.Hacks; + struct HacksComp* hacks = &LocalPlayer_Instances[0].Hacks; if (pressed && !hacks->Enabled) return; if (KeyBind_Claims(KEYBIND_SPEED, key)) hacks->Speeding = pressed; @@ -678,10 +682,10 @@ static void LocalPlayer_InputUp(void* obj, int key) { static void LocalPlayer_SetLocation(struct Entity* e, struct LocationUpdate* update) { struct LocalPlayer* p = (struct LocalPlayer*)e; - LocalInterpComp_SetLocation(&p->Interp, update); + LocalInterpComp_SetLocation(&p->Interp, update, e); } -static void LocalPlayer_Tick(struct Entity* e, double delta) { +static void LocalPlayer_Tick(struct Entity* e, float delta) { struct LocalPlayer* p = (struct LocalPlayer*)e; struct HacksComp* hacks = &p->Hacks; float xMoving = 0, zMoving = 0; @@ -694,7 +698,7 @@ static void LocalPlayer_Tick(struct Entity* e, double delta) { wasOnGround = e->OnGround; LocalInterpComp_AdvanceState(&p->Interp, e); - LocalPlayer_HandleInput(&xMoving, &zMoving); + LocalPlayer_HandleInput(p, &xMoving, &zMoving); hacks->Floating = hacks->Noclip || hacks->Flying; if (!hacks->Floating && hacks->CanBePushed) PhysicsComp_DoEntityPush(e); @@ -712,18 +716,18 @@ static void LocalPlayer_Tick(struct Entity* e, double delta) { e->next.pos = e->Position; e->Position = e->prev.pos; AnimatedComp_Update(e, e->prev.pos, e->next.pos, delta); - TiltComp_Update(&p->Tilt, delta); + TiltComp_Update(p, &p->Tilt, delta); Entity_CheckSkin(&p->Base); - SoundComp_Tick(wasOnGround); + SoundComp_Tick(p, wasOnGround); } -static void LocalPlayer_RenderModel(struct Entity* e, double deltaTime, float t) { +static void LocalPlayer_RenderModel(struct Entity* e, float delta, float t) { struct LocalPlayer* p = (struct LocalPlayer*)e; AnimatedComp_GetCurrent(e, t); - TiltComp_GetCurrent(&p->Tilt, t); + TiltComp_GetCurrent(p, &p->Tilt, t); - if (!Camera.Active->isThirdPerson) return; + if (!Camera.Active->isThirdPerson && p == Entities.CurPlayer) return; Model_Render(e->Model, e); } @@ -732,38 +736,24 @@ static cc_bool LocalPlayer_ShouldRenderName(struct Entity* e) { } static void LocalPlayer_CheckJumpVelocity(void* obj) { - struct LocalPlayer* p = &LocalPlayer_Instance; + struct LocalPlayer* p = (struct LocalPlayer*)obj; if (!HacksComp_CanJumpHigher(&p->Hacks)) { p->Physics.JumpVel = p->Physics.ServerJumpVel; } } -static void LocalPlayer_GetMovement(float* xMoving, float* zMoving) { - if (KeyBind_IsPressed(KEYBIND_FORWARD)) *zMoving -= 1; - if (KeyBind_IsPressed(KEYBIND_BACK)) *zMoving += 1; - if (KeyBind_IsPressed(KEYBIND_LEFT)) *xMoving -= 1; - if (KeyBind_IsPressed(KEYBIND_RIGHT)) *xMoving += 1; - - /* TODO: Move to separate LocalPlayerInputSource */ - if (!Input.JoystickMovement) return; - *xMoving = Math_CosF(Input.JoystickAngle); - *zMoving = Math_SinF(Input.JoystickAngle); -} - static const struct EntityVTABLE localPlayer_VTABLE = { LocalPlayer_Tick, Player_Despawn, LocalPlayer_SetLocation, Entity_GetColor, LocalPlayer_RenderModel, LocalPlayer_ShouldRenderName }; -static void LocalPlayer_Init(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static void LocalPlayer_Init(struct LocalPlayer* p, int index) { struct HacksComp* hacks = &p->Hacks; Entity_Init(&p->Base); Entity_SetName(&p->Base, &Game_Username); Entity_SetSkin(&p->Base, &Game_Username); - Event_Register_(&UserEvents.HackPermsChanged, NULL, LocalPlayer_CheckJumpVelocity); + Event_Register_(&UserEvents.HackPermsChanged, p, LocalPlayer_CheckJumpVelocity); - p->input.GetMovement = LocalPlayer_GetMovement; p->Collisions.Entity = &p->Base; HacksComp_Init(hacks); PhysicsComp_Init(&p->Physics, &p->Base); @@ -774,12 +764,13 @@ static void LocalPlayer_Init(void) { p->Physics.Hacks = &p->Hacks; p->Physics.Collisions = &p->Collisions; p->Base.VTABLE = &localPlayer_VTABLE; + p->index = index; hacks->Enabled = !Game_PureClassic && Options_GetBool(OPT_HACKS_ENABLED, true); /* p->Base.Health = 20; TODO: survival mode stuff */ if (Game_ClassicMode) return; - hacks->SpeedMultiplier = Options_GetFloat(OPT_SPEED_FACTOR, 0.1f, 50.0f, 10.0f); + hacks->SpeedMultiplier = Options_GetFloat(OPT_SPEED_FACTOR, 0.1f, 50.0f, 10.0f); hacks->PushbackPlacing = Options_GetBool(OPT_PUSHBACK_PLACING, false); hacks->NoclipSlide = Options_GetBool(OPT_NOCLIP_SLIDE, false); hacks->WOMStyleHacks = Options_GetBool(OPT_WOM_STYLE_HACKS, false); @@ -789,23 +780,28 @@ static void LocalPlayer_Init(void) { hackPermMsgs = Options_GetBool(OPT_HACK_PERM_MSGS, true); } -void LocalPlayer_ResetJumpVelocity(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +void LocalPlayer_ResetJumpVelocity(struct LocalPlayer* p) { cc_bool higher = HacksComp_CanJumpHigher(&p->Hacks); p->Physics.JumpVel = higher ? p->Physics.UserJumpVel : 0.42f; p->Physics.ServerJumpVel = p->Physics.JumpVel; } -static void LocalPlayer_Reset(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static void LocalPlayer_Reset(struct LocalPlayer* p) { p->ReachDistance = 5.0f; Vec3_Set(p->Base.Velocity, 0,0,0); - LocalPlayer_ResetJumpVelocity(); + LocalPlayer_ResetJumpVelocity(p); } -static void LocalPlayer_OnNewMap(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static void LocalPlayers_Reset(void) { + int i; + for (i = 0; i < Game_NumLocalPlayers; i++) + { + LocalPlayer_Reset(&LocalPlayer_Instances[i]); + } +} + +static void LocalPlayer_OnNewMap(struct LocalPlayer* p) { Vec3_Set(p->Base.Velocity, 0,0,0); Vec3_Set(p->OldVelocity, 0,0,0); @@ -815,9 +811,16 @@ static void LocalPlayer_OnNewMap(void) { p->_warnedZoom = false; } +static void LocalPlayers_OnNewMap(void) { + int i; + for (i = 0; i < Game_NumLocalPlayers; i++) + { + LocalPlayer_OnNewMap(&LocalPlayer_Instances[i]); + } +} + static cc_bool LocalPlayer_IsSolidCollide(BlockID b) { return Blocks.Collide[b] == COLLIDE_SOLID; } -static void LocalPlayer_DoRespawn(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +static void LocalPlayer_DoRespawn(struct LocalPlayer* p) { struct LocationUpdate update; struct AABB bb; Vec3 spawn = p->Spawn; @@ -863,10 +866,9 @@ static void LocalPlayer_DoRespawn(void) { p->Base.OnGround = Entity_TouchesAny(&bb, LocalPlayer_IsSolidCollide); } -cc_bool LocalPlayer_HandleRespawn(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +cc_bool LocalPlayer_HandleRespawn(struct LocalPlayer* p) { if (p->Hacks.CanRespawn) { - LocalPlayer_DoRespawn(); + LocalPlayer_DoRespawn(p); return true; } else if (!p->_warnedRespawn) { p->_warnedRespawn = true; @@ -875,8 +877,7 @@ cc_bool LocalPlayer_HandleRespawn(void) { return false; } -cc_bool LocalPlayer_HandleSetSpawn(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +cc_bool LocalPlayer_HandleSetSpawn(struct LocalPlayer* p) { if (p->Hacks.CanRespawn) { if (!p->Hacks.CanNoclip && !p->Base.OnGround) { @@ -896,11 +897,10 @@ cc_bool LocalPlayer_HandleSetSpawn(void) { p->SpawnYaw = p->Base.Yaw; p->SpawnPitch = p->Base.Pitch; } - return LocalPlayer_HandleRespawn(); + return LocalPlayer_HandleRespawn(p); } -cc_bool LocalPlayer_HandleFly(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +cc_bool LocalPlayer_HandleFly(struct LocalPlayer* p) { if (p->Hacks.CanFly && p->Hacks.Enabled) { HacksComp_SetFlying(&p->Hacks, !p->Hacks.Flying); return true; @@ -911,8 +911,7 @@ cc_bool LocalPlayer_HandleFly(void) { return false; } -cc_bool LocalPlayer_HandleNoclip(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +cc_bool LocalPlayer_HandleNoclip(struct LocalPlayer* p) { if (p->Hacks.CanNoclip && p->Hacks.Enabled) { if (p->Hacks.WOMStyleHacks) return true; /* don't handle this here */ if (p->Hacks.Noclip) p->Base.Velocity.y = 0; @@ -926,8 +925,7 @@ cc_bool LocalPlayer_HandleNoclip(void) { return false; } -cc_bool LocalPlayer_HandleJump(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +cc_bool LocalPlayer_HandleJump(struct LocalPlayer* p) { struct HacksComp* hacks = &p->Hacks; struct PhysicsComp* physics = &p->Physics; int maxJumps; @@ -945,8 +943,7 @@ cc_bool LocalPlayer_HandleJump(void) { return false; } -cc_bool LocalPlayer_CheckCanZoom(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +cc_bool LocalPlayer_CheckCanZoom(struct LocalPlayer* p) { if (p->Hacks.CanFly) return true; if (!p->_warnedZoom) { @@ -956,42 +953,46 @@ cc_bool LocalPlayer_CheckCanZoom(void) { return false; } -void LocalPlayer_MoveToSpawn(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; - struct LocationUpdate update; - - update.flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; - update.pos = p->Spawn; - update.yaw = p->SpawnYaw; - update.pitch = p->SpawnPitch; - - p->Base.VTABLE->SetLocation(&p->Base, &update); +void LocalPlayers_MoveToSpawn(struct LocationUpdate* update) { + struct LocalPlayer* p; + int i; + + for (i = 0; i < Game_NumLocalPlayers; i++) + { + p = &LocalPlayer_Instances[i]; + p->Base.VTABLE->SetLocation(&p->Base, update); + + if (update->flags & LU_HAS_POS) p->Spawn = update->pos; + if (update->flags & LU_HAS_YAW) p->SpawnYaw = update->yaw; + if (update->flags & LU_HAS_PITCH) p->SpawnPitch = update->pitch; + } + /* TODO: This needs to be before new map... */ - Camera.CurrentPos = Camera.Active->GetPosition(0.0f); + Camera.CurrentPos = Camera.Active->GetPosition(Entities.CurPlayer, 0.0f); } -void LocalPlayer_CalcDefaultSpawn(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; +void LocalPlayer_CalcDefaultSpawn(struct LocalPlayer* p, struct LocationUpdate* update) { float x = (World.Width / 2) + 0.5f; float z = (World.Length / 2) + 0.5f; - p->Spawn = Respawn_FindSpawnPosition(x, z, p->Base.Size); - p->SpawnYaw = 0.0f; - p->SpawnPitch = 0.0f; + update->flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; + update->pos = Respawn_FindSpawnPosition(x, z, p->Base.Size); + update->yaw = 0.0f; + update->pitch = 0.0f; } /*########################################################################################################################* *-------------------------------------------------------NetPlayer---------------------------------------------------------* *#########################################################################################################################*/ -struct NetPlayer NetPlayers_List[ENTITIES_SELF_ID]; +struct NetPlayer NetPlayers_List[MAX_NET_PLAYERS]; static void NetPlayer_SetLocation(struct Entity* e, struct LocationUpdate* update) { struct NetPlayer* p = (struct NetPlayer*)e; NetInterpComp_SetLocation(&p->Interp, update, e); } -static void NetPlayer_Tick(struct Entity* e, double delta) { +static void NetPlayer_Tick(struct Entity* e, float delta) { struct NetPlayer* p = (struct NetPlayer*)e; NetInterpComp_AdvanceState(&p->Interp, e); @@ -999,7 +1000,7 @@ static void NetPlayer_Tick(struct Entity* e, double delta) { AnimatedComp_Update(e, e->prev.pos, e->next.pos, delta); } -static void NetPlayer_RenderModel(struct Entity* e, double deltaTime, float t) { +static void NetPlayer_RenderModel(struct Entity* e, float delta, float t) { Vec3_Lerp(&e->Position, &e->prev.pos, &e->next.pos, t); Entity_LerpAngles(e, t); @@ -1025,6 +1026,7 @@ static const struct EntityVTABLE netPlayer_VTABLE = { void NetPlayer_Init(struct NetPlayer* p) { Mem_Set(p, 0, sizeof(struct NetPlayer)); Entity_Init(&p->Base); + p->Base.Flags |= ENTITY_FLAG_CLASSIC_ADJUST; p->Base.VTABLE = &netPlayer_VTABLE; } @@ -1033,6 +1035,7 @@ void NetPlayer_Init(struct NetPlayer* p) { *---------------------------------------------------Entities component----------------------------------------------------* *#########################################################################################################################*/ static void Entities_Init(void) { + int i; Event_Register_(&GfxEvents.ContextLost, NULL, Entities_ContextLost); Event_Register_(&InputEvents.Down, NULL, LocalPlayer_InputDown); Event_Register_(&InputEvents.Up, NULL, LocalPlayer_InputUp); @@ -1045,8 +1048,12 @@ static void Entities_Init(void) { ShadowMode_Names, Array_Elems(ShadowMode_Names)); if (Game_ClassicMode) Entities.ShadowsMode = SHADOW_MODE_NONE; - Entities.List[ENTITIES_SELF_ID] = &LocalPlayer_Instance.Base; - LocalPlayer_Init(); + for (i = 0; i < Game_NumLocalPlayers; i++) + { + LocalPlayer_Init(&LocalPlayer_Instances[i], i); + Entities.List[MAX_NET_PLAYERS + i] = &LocalPlayer_Instances[i].Base; + } + Entities.CurPlayer = &LocalPlayer_Instances[0]; } static void Entities_Free(void) { @@ -1060,6 +1067,6 @@ static void Entities_Free(void) { struct IGameComponent Entities_Component = { Entities_Init, /* Init */ Entities_Free, /* Free */ - LocalPlayer_Reset, /* Reset */ - LocalPlayer_OnNewMap, /* OnNewMap */ + LocalPlayers_Reset, /* Reset */ + LocalPlayers_OnNewMap, /* OnNewMap */ }; diff --git a/src/Entity.h b/src/Entity.h index 4c6ea5d2e..295d168aa 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -11,12 +11,21 @@ struct Model; struct IGameComponent; struct ScheduledTask; +struct LocalPlayer; + extern struct IGameComponent TabList_Component; extern struct IGameComponent Entities_Component; +#ifdef CC_BUILD_SPLITSCREEN +#define MAX_LOCAL_PLAYERS 4 +#else +#define MAX_LOCAL_PLAYERS 1 +#endif +#define MAX_NET_PLAYERS 255 + /* Offset used to avoid floating point roundoff errors. */ #define ENTITY_ADJUSTMENT 0.001f -#define ENTITIES_MAX_COUNT 256 +#define ENTITIES_MAX_COUNT (MAX_NET_PLAYERS + MAX_LOCAL_PLAYERS) #define ENTITIES_SELF_ID 255 enum NameMode { @@ -66,11 +75,11 @@ struct EntityLocation { Vec3 pos; float pitch, yaw, rotX, rotY, rotZ; }; struct Entity; struct EntityVTABLE { - void (*Tick)(struct Entity* e, double delta); + void (*Tick)(struct Entity* e, float delta); void (*Despawn)(struct Entity* e); void (*SetLocation)(struct Entity* e, struct LocationUpdate* update); PackedCol (*GetCol)(struct Entity* e); - void (*RenderModel)(struct Entity* e, double deltaTime, float t); + void (*RenderModel)(struct Entity* e, float delta, float t); cc_bool (*ShouldRenderName)(struct Entity* e); }; @@ -87,6 +96,9 @@ struct EntityVTABLE { /* And therefore trying to access the ModelVB Field in entity struct instances created by the CEF plugin */ /* results in attempting to read or write data from potentially invalid memory */ #define ENTITY_FLAG_HAS_MODELVB 0x02 +/* Whether in classic mode, to slightly adjust this entity downwards when rendering it */ +/* to replicate the behaviour of the original vanilla classic client */ +#define ENTITY_FLAG_CLASSIC_ADJUST 0x04 /* Contains a model, along with position, velocity, and rotation. May also contain other fields and properties. */ struct Entity { @@ -159,16 +171,18 @@ void Entity_LerpAngles(struct Entity* e, float t); CC_VAR extern struct _EntitiesData { struct Entity* List[ENTITIES_MAX_COUNT]; cc_uint8 NamesMode, ShadowsMode; + struct LocalPlayer* CurPlayer; } Entities; /* Ticks all entities */ void Entities_Tick(struct ScheduledTask* task); /* Renders all entities */ -void Entities_RenderModels(double delta, float t); +void Entities_RenderModels(float delta, float t); /* Removes the given entity, raising EntityEvents.Removed event */ void Entities_Remove(EntityID id); /* Gets the ID of the closest entity to the given entity */ -EntityID Entities_GetClosest(struct Entity* src); +/* Returns -1 if there is no other entity nearby */ +int Entities_GetClosest(struct Entity* src); #define TABLIST_MAX_NAMES 256 /* Data for all entries in tab list */ @@ -209,13 +223,14 @@ struct NetPlayer { struct NetInterpComp Interp; }; CC_API void NetPlayer_Init(struct NetPlayer* player); -extern struct NetPlayer NetPlayers_List[ENTITIES_SELF_ID]; +extern struct NetPlayer NetPlayers_List[MAX_NET_PLAYERS]; struct LocalPlayerInput; struct LocalPlayerInput { - void (*GetMovement)(float* xMoving, float* zMoving); + void (*GetMovement)(struct LocalPlayer* p, float* xMoving, float* zMoving); struct LocalPlayerInput* next; }; +void LocalPlayerInput_Add(struct LocalPlayerInput* source); /* Represents the user/player's own entity. */ struct LocalPlayer { @@ -228,23 +243,23 @@ struct LocalPlayer { struct CollisionsComp Collisions; struct PhysicsComp Physics; cc_bool _warnedRespawn, _warnedFly, _warnedNoclip, _warnedZoom; - struct LocalPlayerInput input; + cc_uint8 index; }; -extern struct LocalPlayer LocalPlayer_Instance; +extern struct LocalPlayer LocalPlayer_Instances[MAX_LOCAL_PLAYERS]; /* Returns how high (in blocks) the player can jump. */ -float LocalPlayer_JumpHeight(void); +float LocalPlayer_JumpHeight(struct LocalPlayer* p); /* Interpolates current position and orientation between Interp.Prev and Interp.Next */ -void LocalPlayer_SetInterpPosition(float t); -void LocalPlayer_ResetJumpVelocity(void); -cc_bool LocalPlayer_CheckCanZoom(void); +void LocalPlayer_SetInterpPosition(struct LocalPlayer* p, float t); +void LocalPlayer_ResetJumpVelocity(struct LocalPlayer* p); +cc_bool LocalPlayer_CheckCanZoom(struct LocalPlayer* p); /* Moves local player back to spawn point. */ -void LocalPlayer_MoveToSpawn(void); -void LocalPlayer_CalcDefaultSpawn(void); +void LocalPlayers_MoveToSpawn(struct LocationUpdate* update); +void LocalPlayer_CalcDefaultSpawn(struct LocalPlayer* p, struct LocationUpdate* update); -cc_bool LocalPlayer_HandleRespawn(void); -cc_bool LocalPlayer_HandleSetSpawn(void); -cc_bool LocalPlayer_HandleFly(void); -cc_bool LocalPlayer_HandleNoclip(void); -cc_bool LocalPlayer_HandleJump(void); +cc_bool LocalPlayer_HandleRespawn(struct LocalPlayer* p); +cc_bool LocalPlayer_HandleSetSpawn(struct LocalPlayer* p); +cc_bool LocalPlayer_HandleFly(struct LocalPlayer* p); +cc_bool LocalPlayer_HandleNoclip(struct LocalPlayer* p); +cc_bool LocalPlayer_HandleJump(struct LocalPlayer* p); #endif diff --git a/src/EntityComponents.c b/src/EntityComponents.c index 4d222ff3b..962713385 100644 --- a/src/EntityComponents.c +++ b/src/EntityComponents.c @@ -57,7 +57,7 @@ void AnimatedComp_Init(struct AnimatedComp* anim) { anim->BobStrength = 1.0f; anim->BobStrengthO = 1.0f; anim->BobStrengthN = 1.0f; } -void AnimatedComp_Update(struct Entity* e, Vec3 oldPos, Vec3 newPos, double delta) { +void AnimatedComp_Update(struct Entity* e, Vec3 oldPos, Vec3 newPos, float delta) { struct AnimatedComp* anim = &e->Anim; float dx = newPos.x - oldPos.x; float dz = newPos.z - oldPos.z; @@ -71,9 +71,9 @@ void AnimatedComp_Update(struct Entity* e, Vec3 oldPos, Vec3 newPos, double delt if (distance > 0.05f) { walkDelta = distance * 2 * (float)(20 * delta); anim->WalkTimeN += walkDelta; - anim->SwingN += (float)delta * 3; + anim->SwingN += delta * 3; } else { - anim->SwingN -= (float)delta * 3; + anim->SwingN -= delta * 3; } Math_Clamp(anim->SwingN, 0.0f, 1.0f); @@ -120,8 +120,7 @@ void TiltComp_Init(struct TiltComp* anim) { anim->VelTiltStrengthO = 1.0f; anim->VelTiltStrengthN = 1.0f; } -void TiltComp_Update(struct TiltComp* anim, double delta) { - struct LocalPlayer* p = &LocalPlayer_Instance; +void TiltComp_Update(struct LocalPlayer* p, struct TiltComp* anim, float delta) { int i; anim->VelTiltStrengthO = anim->VelTiltStrengthN; @@ -131,8 +130,7 @@ void TiltComp_Update(struct TiltComp* anim, double delta) { } } -void TiltComp_GetCurrent(struct TiltComp* anim, float t) { - struct LocalPlayer* p = &LocalPlayer_Instance; +void TiltComp_GetCurrent(struct LocalPlayer* p, struct TiltComp* anim, float t) { struct AnimatedComp* pAnim = &p->Base.Anim; anim->VelTiltStrength = Math_Lerp(anim->VelTiltStrengthO, anim->VelTiltStrengthN, t); @@ -427,8 +425,7 @@ void NetInterpComp_AdvanceState(struct NetInterpComp* interp, struct Entity* e) /*########################################################################################################################* *-----------------------------------------------LocalInterpolationComponent-----------------------------------------------* *#########################################################################################################################*/ -static void LocalInterpComp_SetPosition(struct LocationUpdate* update, int mode) { - struct Entity* e = &LocalPlayer_Instance.Base; +static void LocalInterpComp_SetPosition(struct Entity* e, struct LocationUpdate* update, int mode) { float yOffset; if (mode == LU_POS_ABSOLUTE_INSTANT || mode == LU_POS_ABSOLUTE_SMOOTH) { @@ -455,15 +452,14 @@ static void LocalInterpComp_Angle(float* prev, float* next, float value, cc_bool if (!interpolate) *prev = value; } -void LocalInterpComp_SetLocation(struct InterpComp* interp, struct LocationUpdate* update) { - struct Entity* e = &LocalPlayer_Instance.Base; +void LocalInterpComp_SetLocation(struct InterpComp* interp, struct LocationUpdate* update, struct Entity* e) { struct EntityLocation* prev = &e->prev; struct EntityLocation* next = &e->next; cc_uint8 flags = update->flags; cc_bool interpolate = flags & LU_ORI_INTERPOLATE; if (flags & LU_HAS_POS) { - LocalInterpComp_SetPosition(update, flags & LU_POS_MODEMASK); + LocalInterpComp_SetPosition(e, update, flags & LU_POS_MODEMASK); } if (flags & LU_HAS_PITCH) { LocalInterpComp_Angle(&prev->pitch, &next->pitch, update->pitch, interpolate); @@ -1013,7 +1009,7 @@ static double PhysicsComp_YPosAt(int t, float u) { double PhysicsComp_CalcMaxHeight(float u) { /* equation below comes from solving diff(x(t, u))= 0 */ /* We only work in discrete timesteps, so test both rounded up and down */ - double t = 49.49831645 * Math_Log(0.247483075 * u + 0.9899323); + double t = 34.30961849 * Math_Log2(0.247483075 * u + 0.9899323); double value_floor = PhysicsComp_YPosAt((int)t, u); double value_ceil = PhysicsComp_YPosAt((int)t + 1, u); return max(value_floor, value_ceil); @@ -1134,18 +1130,17 @@ static cc_bool SoundComp_ShouldPlay(struct LocalPlayer* p, Vec3 soundPos) { /* have our legs just crossed over the '0' point? */ if (Camera.Active->isThirdPerson) { - oldLegRot = (float)Math_Cos(p->Base.Anim.WalkTimeO); - newLegRot = (float)Math_Cos(p->Base.Anim.WalkTimeN); + oldLegRot = Math_CosF(p->Base.Anim.WalkTimeO); + newLegRot = Math_CosF(p->Base.Anim.WalkTimeN); } else { - oldLegRot = (float)Math_Sin(p->Base.Anim.WalkTimeO); - newLegRot = (float)Math_Sin(p->Base.Anim.WalkTimeN); + oldLegRot = Math_SinF(p->Base.Anim.WalkTimeO); + newLegRot = Math_SinF(p->Base.Anim.WalkTimeN); } return Math_Sign(oldLegRot) != Math_Sign(newLegRot); } -void SoundComp_Tick(cc_bool wasOnGround) { - struct LocalPlayer* p = &LocalPlayer_Instance; - Vec3 soundPos = p->Base.next.pos; +void SoundComp_Tick(struct LocalPlayer* p, cc_bool wasOnGround) { + Vec3 soundPos = p->Base.next.pos; SoundComp_GetSound(p); if (!sounds_anyNonAir) soundPos = Vec3_BigPos(); diff --git a/src/EntityComponents.h b/src/EntityComponents.h index f4cb711e4..1fd394c2d 100644 --- a/src/EntityComponents.h +++ b/src/EntityComponents.h @@ -8,6 +8,7 @@ struct Entity; struct LocationUpdate; +struct LocalPlayer; /* Entity component that performs model animation depending on movement speed and time */ struct AnimatedComp { @@ -20,7 +21,7 @@ struct AnimatedComp { }; void AnimatedComp_Init(struct AnimatedComp* anim); -void AnimatedComp_Update(struct Entity* entity, Vec3 oldPos, Vec3 newPos, double delta); +void AnimatedComp_Update(struct Entity* entity, Vec3 oldPos, Vec3 newPos, float delta); void AnimatedComp_GetCurrent(struct Entity* entity, float t); /* Entity component that performs tilt animation depending on movement speed and time */ @@ -30,8 +31,8 @@ struct TiltComp { }; void TiltComp_Init(struct TiltComp* anim); -void TiltComp_Update(struct TiltComp* anim, double delta); -void TiltComp_GetCurrent(struct TiltComp* anim, float t); +void TiltComp_Update(struct LocalPlayer* p, struct TiltComp* anim, float delta); +void TiltComp_GetCurrent(struct LocalPlayer* p, struct TiltComp* anim, float t); /* Entity component that performs management of hack states */ struct HacksComp { @@ -81,7 +82,7 @@ float HacksComp_CalcSpeedFactor(struct HacksComp* hacks, cc_bool canSpeed); /* Base entity component that performs interpolation of position and orientation */ struct InterpComp { InterpComp_Layout }; -void LocalInterpComp_SetLocation(struct InterpComp* interp, struct LocationUpdate* update); +void LocalInterpComp_SetLocation(struct InterpComp* interp, struct LocationUpdate* update, struct Entity* e); void LocalInterpComp_AdvanceState(struct InterpComp* interp, struct Entity* e); /* Represents a network orientation state */ @@ -130,5 +131,5 @@ double PhysicsComp_CalcMaxHeight(float u); void PhysicsComp_DoEntityPush(struct Entity* entity); /* Entity component that plays block step sounds */ -void SoundComp_Tick(cc_bool wasOnGround); +void SoundComp_Tick(struct LocalPlayer* p, cc_bool wasOnGround); #endif diff --git a/src/EntityRenderers.c b/src/EntityRenderers.c index 4a35f8eb2..f673a8904 100644 --- a/src/EntityRenderers.c +++ b/src/EntityRenderers.c @@ -19,7 +19,7 @@ static cc_bool shadows_boundTex; static GfxResourceID shadows_VB; static GfxResourceID shadows_tex; static float shadow_radius, shadow_uvScale; -struct ShadowData { float y; BlockID Block; cc_uint8 A; }; +struct ShadowData { float y; BlockID block; cc_uint8 alpha; }; /* Circle shadows extend at most 4 blocks vertically */ #define SHADOW_MAX_RANGE 4 @@ -54,7 +54,7 @@ static void EntityShadow_DrawCoords(struct VertexTextured** vertices, struct Ent z2 = min(z2, cen.z + shadow_radius); v2 = v2 <= 1.0f ? v2 : 1.0f; v = *vertices; - col = PackedCol_Make(255, 255, 255, data->A); + col = PackedCol_Make(255, 255, 255, data->alpha); v->x = x1; v->y = data->y; v->z = z1; v->Col = col; v->U = u1; v->V = v1; v++; v->x = x2; v->y = data->y; v->z = z1; v->Col = col; v->U = u2; v->V = v1; v++; @@ -83,13 +83,13 @@ static void EntityShadow_DrawCircle(struct VertexTextured** vertices, struct Ent Vec3 min, max, nMin, nMax; int i; x = (float)Math_Floor(x); z = (float)Math_Floor(z); - min = Blocks.MinBB[data[0].Block]; max = Blocks.MaxBB[data[0].Block]; + min = Blocks.MinBB[data[0].block]; max = Blocks.MaxBB[data[0].block]; EntityShadow_DrawCoords(vertices, e, &data[0], x + min.x, z + min.z, x + max.x, z + max.z); for (i = 1; i < 4; i++) { - if (data[i].Block == BLOCK_AIR) return; - nMin = Blocks.MinBB[data[i].Block]; nMax = Blocks.MaxBB[data[i].Block]; + if (data[i].block == BLOCK_AIR) return; + nMin = Blocks.MinBB[data[i].block]; nMax = Blocks.MaxBB[data[i].block]; EntityShadow_DrawCoords(vertices, e, &data[i], x + min.x, z + nMin.z, x + max.x, z + min.z); EntityShadow_DrawCoords(vertices, e, &data[i], x + min.x, z + max.z, x + max.x, z + nMax.z); @@ -103,11 +103,12 @@ static void EntityShadow_DrawCircle(struct VertexTextured** vertices, struct Ent static void EntityShadow_CalcAlpha(float playerY, struct ShadowData* data) { float height = playerY - data->y; if (height <= 6.0f) { - data->A = (cc_uint8)(160 - 160 * height / 6.0f); - data->y += 1.0f / 64.0f; return; + data->alpha = (cc_uint8)(160 - 160 * height / 6.0f); + data->y += 1.0f / 64.0f; + return; } - data->A = 0; + data->alpha = 0; if (height <= 16.0f) data->y += 1.0f / 64.0f; else if (height <= 32.0f) data->y += 1.0f / 16.0f; else if (height <= 96.0f) data->y += 1.0f / 8.0f; @@ -144,7 +145,7 @@ static cc_bool EntityShadow_GetBlocks(struct Entity* e, int x, int y, int z, str topY = y + Blocks.MaxBB[block].y; if (topY >= posY + 0.01f) continue; - cur->Block = block; cur->y = topY; + cur->block = block; cur->y = topY; EntityShadow_CalcAlpha(posY, cur); i++; cur++; @@ -154,7 +155,7 @@ static cc_bool EntityShadow_GetBlocks(struct Entity* e, int x, int y, int z, str } if (i < 4) { - cur->Block = Env.EdgeBlock; cur->y = 0.0f; + cur->block = Env.EdgeBlock; cur->y = 0.0f; EntityShadow_CalcAlpha(posY, cur); i++; cur++; } @@ -188,16 +189,16 @@ static void EntityShadow_Draw(struct Entity* e) { x1 = Math_Floor(pos.x - shadow_radius); z1 = Math_Floor(pos.z - shadow_radius); x2 = Math_Floor(pos.x + shadow_radius); z2 = Math_Floor(pos.z + shadow_radius); - if (EntityShadow_GetBlocks(e, x1, y, z1, data) && data[0].A > 0) { + if (EntityShadow_GetBlocks(e, x1, y, z1, data) && data[0].alpha > 0) { EntityShadow_DrawCircle(&ptr, e, data, (float)x1, (float)z1); } - if (x1 != x2 && EntityShadow_GetBlocks(e, x2, y, z1, data) && data[0].A > 0) { + if (x1 != x2 && EntityShadow_GetBlocks(e, x2, y, z1, data) && data[0].alpha > 0) { EntityShadow_DrawCircle(&ptr, e, data, (float)x2, (float)z1); } - if (z1 != z2 && EntityShadow_GetBlocks(e, x1, y, z2, data) && data[0].A > 0) { + if (z1 != z2 && EntityShadow_GetBlocks(e, x1, y, z2, data) && data[0].alpha > 0) { EntityShadow_DrawCircle(&ptr, e, data, (float)x1, (float)z2); } - if (x1 != x2 && z1 != z2 && EntityShadow_GetBlocks(e, x2, y, z2, data) && data[0].A > 0) { + if (x1 != x2 && z1 != z2 && EntityShadow_GetBlocks(e, x2, y, z2, data) && data[0].alpha > 0) { EntityShadow_DrawCircle(&ptr, e, data, (float)x2, (float)z2); } } @@ -232,9 +233,9 @@ static void EntityShadows_MakeTexture(void) { BitmapCol* row = Bitmap_GetRow(&bmp, y); for (x = 0; x < sh_size; x++) { - double dist = - (sh_half - (x + 0.5)) * (sh_half - (x + 0.5)) + - (sh_half - (y + 0.5)) * (sh_half - (y + 0.5)); + float dist = + (sh_half - (x + 0.5f)) * (sh_half - (x + 0.5f)) + + (sh_half - (y + 0.5f)) * (sh_half - (y + 0.5f)); row[x] = dist < sh_half * sh_half ? color : 0; } } @@ -242,6 +243,7 @@ static void EntityShadows_MakeTexture(void) { } void EntityShadows_Render(void) { + struct Entity* e; int i; if (Entities.ShadowsMode == SHADOW_MODE_NONE) return; @@ -256,13 +258,14 @@ void EntityShadows_Render(void) { Gfx_SetAlphaBlending(true); Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); - EntityShadow_Draw(Entities.List[ENTITIES_SELF_ID]); + EntityShadow_Draw(&Entities.CurPlayer->Base); if (Entities.ShadowsMode == SHADOW_MODE_CIRCLE_ALL) { - for (i = 0; i < ENTITIES_SELF_ID; i++) + for (i = 0; i < ENTITIES_MAX_COUNT; i++) { - if (!Entities.List[i] || !Entities.List[i]->ShouldRender) continue; - EntityShadow_Draw(Entities.List[i]); + e = Entities.List[i]; + if (!e || !e->ShouldRender || e == &Entities.CurPlayer->Base) continue; + EntityShadow_Draw(e); } } @@ -346,9 +349,9 @@ static void DrawName(struct Entity* e) { scale = e->ModelScale.y; scale = scale > 1.0f ? (1.0f/70.0f) : (scale/70.0f); - size.x = e->NameTex.Width * scale; size.y = e->NameTex.Height * scale; + size.x = e->NameTex.width * scale; size.y = e->NameTex.height * scale; - if (Entities.NamesMode == NAME_MODE_ALL_UNSCALED && LocalPlayer_Instance.Hacks.CanSeeAllNames) { + if (Entities.NamesMode == NAME_MODE_ALL_UNSCALED && Entities.CurPlayer->Hacks.CanSeeAllNames) { Matrix_Mul(&mat, &Gfx.View, &Gfx.Projection); /* TODO: This mul is slow, avoid it */ /* Get W component of transformed position */ scale = pos.x * mat.row1.w + pos.y * mat.row2.w + pos.z * mat.row3.w + mat.row4.w; @@ -373,10 +376,10 @@ void EntityNames_Delete(struct Entity* e) { /*########################################################################################################################* *-----------------------------------------------------Names rendering-----------------------------------------------------* *#########################################################################################################################*/ -static EntityID closestEntityId; +static int closestEntityId; void EntityNames_Render(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; + struct LocalPlayer* p = Entities.CurPlayer; cc_bool hadFog; int i; @@ -391,9 +394,7 @@ void EntityNames_Render(void) { for (i = 0; i < ENTITIES_MAX_COUNT; i++) { if (!Entities.List[i]) continue; - if (i != closestEntityId || i == ENTITIES_SELF_ID) { - DrawName(Entities.List[i]); - } + if (i != closestEntityId) DrawName(Entities.List[i]); } Gfx_SetAlphaTest(false); @@ -401,7 +402,7 @@ void EntityNames_Render(void) { } void EntityNames_RenderHovered(void) { - struct LocalPlayer* p = &LocalPlayer_Instance; + struct LocalPlayer* p = Entities.CurPlayer; cc_bool allNames, hadFog; int i; @@ -417,7 +418,7 @@ void EntityNames_RenderHovered(void) { for (i = 0; i < ENTITIES_MAX_COUNT; i++) { if (!Entities.List[i]) continue; - if ((i == closestEntityId || allNames) && i != ENTITIES_SELF_ID) { + if ((i == closestEntityId || allNames) && Entities.List[i] != &p->Base) { DrawName(Entities.List[i]); } } diff --git a/src/EnvRenderer.c b/src/EnvRenderer.c index dca94780d..6f5fcc35e 100644 --- a/src/EnvRenderer.c +++ b/src/EnvRenderer.c @@ -18,15 +18,15 @@ #include "Camera.h" #include "Particle.h" #include "Options.h" +#include "Entity.h" cc_bool EnvRenderer_Legacy, EnvRenderer_Minimal; static float CalcBlendFactor(float x) { - /* return -0.05 + 0.22 * (Math_Log(x) * 0.25f); */ - double blend = -0.13 + 0.28 * (Math_Log(x) * 0.25); - if (blend < 0.0) blend = 0.0; - if (blend > 1.0) blend = 1.0; - return (float)blend; + float blend = -0.13f + 0.28f * ((float)Math_Log2(x) * 0.17329f); + if (blend < 0.0f) blend = 0.0f; + if (blend > 1.0f) blend = 1.0f; + return blend; } #define EnvRenderer_AxisSize() (EnvRenderer_Legacy ? 128 : 2048) @@ -40,21 +40,25 @@ static int CalcNumVertices(int axis1Len, int axis2Len) { /*########################################################################################################################* *------------------------------------------------------------Fog----------------------------------------------------------* *#########################################################################################################################*/ -static void CalcFog(float* density, PackedCol* color) { +static cc_bool CameraInsideBlock(BlockID block, IVec3* coords) { + struct AABB blockBB; Vec3 pos; + IVec3_ToVec3(&pos, coords); /* pos = coords; */ + + Vec3_Add(&blockBB.Min, &pos, &Blocks.MinBB[block]); + Vec3_Add(&blockBB.Max, &pos, &Blocks.MaxBB[block]); + return AABB_ContainsPoint(&blockBB, &Camera.CurrentPos); +} + +static void CalcFog(float* density, PackedCol* color) { IVec3 coords; BlockID block; - struct AABB blockBB; float blend; IVec3_Floor(&coords, &Camera.CurrentPos); /* coords = floor(camera_pos); */ - IVec3_ToVec3(&pos, &coords); /* pos = coords; */ - block = World_SafeGetBlock(coords.x, coords.y, coords.z); - Vec3_Add(&blockBB.Min, &pos, &Blocks.MinBB[block]); - Vec3_Add(&blockBB.Max, &pos, &Blocks.MaxBB[block]); - if (AABB_ContainsPoint(&blockBB, &Camera.CurrentPos) && Blocks.FogDensity[block]) { + if (Blocks.FogDensity[block] && CameraInsideBlock(block, &coords)) { *density = Blocks.FogDensity[block]; *color = Blocks.FogCol[block]; } else { @@ -73,7 +77,7 @@ static void UpdateFogMinimal(float fogDensity) { /* Exp fog mode: f = e^(-density*coord) */ /* Solve coord for f = 0.05 (good approx for fog end) */ /* i.e. log(0.05) = -density * coord */ - #define LOG_005 -2.99573227355399 + #define LOG_005 -2.99573227355399f dist = (int)(LOG_005 / -fogDensity); Game_SetViewDistance(min(dist, Game_UserViewDistance)); @@ -83,7 +87,7 @@ static void UpdateFogMinimal(float fogDensity) { } static void UpdateFogNormal(float fogDensity, PackedCol fogColor) { - double density; + float density; if (fogDensity != 0.0f) { Gfx_SetFogMode(FOG_EXP); @@ -96,10 +100,10 @@ static void UpdateFogNormal(float fogDensity, PackedCol fogColor) { 0.99=z/end --> z=end*0.99 therefore d = -ln(0.01)/(end*0.99) */ - #define LOG_001 -4.60517018598809 + #define LOG_001 -4.60517018598809f - density = -(LOG_001) / (Game_ViewDistance * 0.99); - Gfx_SetFogDensity((float)density); + density = -LOG_001 / (Game_ViewDistance * 0.99f); + Gfx_SetFogDensity(density); } else { Gfx_SetFogMode(FOG_LINEAR); Gfx_SetFogEnd((float)Game_ViewDistance); @@ -114,7 +118,7 @@ void EnvRenderer_UpdateFog(void) { if (!World.Loaded) return; CalcFog(&fogDensity, &fogColor); - Gfx_ClearCol(fogColor); + Gfx_ClearColor(fogColor); if (EnvRenderer_Minimal) { UpdateFogMinimal(fogDensity); @@ -322,7 +326,8 @@ void EnvRenderer_RenderSkybox(void) { /* Rotate around camera */ pos = Camera.CurrentPos; Vec3_Set(Camera.CurrentPos, 0,0,0); - Camera.Active->GetView(&view); Matrix_MulBy(&m, &view); + Camera.Active->GetView(Entities.CurPlayer, &view); + Matrix_MulBy(&m, &view); Camera.CurrentPos = pos; Gfx_LoadMatrix(MATRIX_VIEW, &m); @@ -338,7 +343,7 @@ void EnvRenderer_RenderSkybox(void) { *#########################################################################################################################*/ cc_int16* Weather_Heightmap; static GfxResourceID rain_tex, snow_tex, weather_vb; -static double weather_accumulator; +static float weather_accumulator; static IVec3 lastPos; #define WEATHER_EXTENT 4 @@ -429,7 +434,7 @@ static float CalcRainAlphaAt(float x) { struct RainCoord { int dx, dz; float y; }; static RNGState snowDirRng; -void EnvRenderer_RenderWeather(double deltaTime) { +void EnvRenderer_RenderWeather(float delta) { struct RainCoord coords[WEATHER_RANGE * WEATHER_RANGE]; int i, weather, numCoords = 0; struct VertexTextured* v; @@ -460,8 +465,8 @@ void EnvRenderer_RenderWeather(double deltaTime) { pos.y += 64; pos.y = max(World.Height, pos.y); - weather_accumulator += deltaTime; - particles = weather == WEATHER_RAINY && (weather_accumulator >= 0.25 || moved); + weather_accumulator += delta; + particles = weather == WEATHER_RAINY && (weather_accumulator >= 0.25f || moved); for (dx = -WEATHER_EXTENT; dx <= WEATHER_EXTENT; dx++) { for (dz = -WEATHER_EXTENT; dz <= WEATHER_EXTENT; dz++) { @@ -600,7 +605,7 @@ static void MakeBorderTex(GfxResourceID* texId, BlockID block) { static Rect2D EnvRenderer_Rect(int x, int y, int width, int height) { Rect2D r; - r.x = x; r.y = y; r.Width = width; r.Height = height; + r.x = x; r.y = y; r.width = width; r.height = height; return r; } @@ -709,7 +714,7 @@ static void UpdateMapSides(void) { sides_vertices = 0; for (i = 0; i < 4; i++) { r = rects[i]; - sides_vertices += CalcNumVertices(r.Width, r.Height); /* YQuads outside */ + sides_vertices += CalcNumVertices(r.width, r.height); /* YQuads outside */ } y = Env_SidesHeight; @@ -725,7 +730,7 @@ static void UpdateMapSides(void) { for (i = 0; i < 4; i++) { r = rects[i]; - DrawBorderY(r.x, r.y, r.x + r.Width, r.y + r.Height, (float)y, color, + DrawBorderY(r.x, r.y, r.x + r.width, r.y + r.height, (float)y, color, 0, Borders_YOffset(block), &data); } @@ -760,7 +765,7 @@ static void UpdateMapEdges(void) { edges_vertices = 0; for (i = 0; i < 4; i++) { r = rects[i]; - edges_vertices += CalcNumVertices(r.Width, r.Height); /* YPlanes outside */ + edges_vertices += CalcNumVertices(r.width, r.height); /* YPlanes outside */ } data = (struct VertexTextured*)Gfx_RecreateAndLockVb(&edges_vb, VERTEX_FORMAT_TEXTURED, edges_vertices); @@ -772,7 +777,7 @@ static void UpdateMapEdges(void) { y = (float)Env.EdgeHeight; for (i = 0; i < 4; i++) { r = rects[i]; - DrawBorderY(r.x, r.y, r.x + r.Width, r.y + r.Height, y, color, + DrawBorderY(r.x, r.y, r.x + r.width, r.y + r.height, y, color, Borders_HorOffset(block), Borders_YOffset(block), &data); } Gfx_UnlockVb(edges_vb); diff --git a/src/EnvRenderer.h b/src/EnvRenderer.h index 4be63663e..b27344178 100644 --- a/src/EnvRenderer.h +++ b/src/EnvRenderer.h @@ -31,7 +31,7 @@ extern cc_int16* Weather_Heightmap; /* Called when a block is changed to update internal weather state. */ void EnvRenderer_OnBlockChanged(int x, int y, int z, BlockID oldBlock, BlockID newBlock); /* Renders rainfall/snowfall weather. */ -void EnvRenderer_RenderWeather(double deltaTime); +void EnvRenderer_RenderWeather(float delta); /* Whether large quads are broken down into smaller quads. */ /* This makes them have less rendering issues when using vertex fog. */ diff --git a/src/Errors.h b/src/Errors.h index 6b2350284..25abd7206 100644 --- a/src/Errors.h +++ b/src/Errors.h @@ -135,5 +135,6 @@ enum CC_ERRORS { HTTP_ERR_TRUNCATED = 0xCCDED06CUL, /* HTTP respone header was truncated due to being too long */ SSL_ERR_CONTEXT_DEAD = 0xCCDED070UL, /* Server shutdown the SSL context and it must be recreated */ + PNG_ERR_16BITSAMPLES = 0xCCDED071UL, /* Image uses 16 bit samples, which is unimplemented */ }; #endif diff --git a/src/Event.c b/src/Event.c index 8f0981753..91cca9c68 100644 --- a/src/Event.c +++ b/src/Event.c @@ -108,7 +108,7 @@ void Event_UnregisterAll(void) { PointerEvents.Up.Count = 0; PointerEvents.RawMoved.Count = 0; - ControllerEvents.RawMoved.Count = 0; + ControllerEvents.AxisUpdate.Count = 0; NetEvents.Connected.Count = 0; NetEvents.Disconnected.Count = 0; @@ -178,6 +178,13 @@ void Event_RaiseRawMove(struct Event_RawMove* handlers, float xDelta, float yDel } } +void Event_RaisePadAxis(struct Event_PadAxis* handlers, int port, int axis, float x, float y) { + int i; + for (i = 0; i < handlers->Count; i++) { + handlers->Handlers[i](handlers->Objs[i], port, axis, x, y); + } +} + void Event_RaisePluginMessage(struct Event_PluginMessage* handlers, cc_uint8 channel, cc_uint8* data) { int i; for (i = 0; i < handlers->Count; i++) { diff --git a/src/Event.h b/src/Event.h index 9440063c8..398a6dfee 100644 --- a/src/Event.h +++ b/src/Event.h @@ -63,6 +63,12 @@ struct Event_RawMove { void* Objs[EVENT_MAX_CALLBACKS]; int Count; }; +typedef void (*Event_PadAxis_Callback)(void* obj, int port, int axis, float x, float y); +struct Event_PadAxis { + Event_PadAxis_Callback Handlers[EVENT_MAX_CALLBACKS]; + void* Objs[EVENT_MAX_CALLBACKS]; int Count; +}; + /* "data" will be 64 bytes in length. */ typedef void (*Event_PluginMessage_Callback)(void* obj, cc_uint8 channel, cc_uint8* data); struct Event_PluginMessage { @@ -101,6 +107,8 @@ void Event_RaiseInput(struct Event_Input* handlers, int key, cc_bool repeating); void Event_RaiseString(struct Event_String* handlers, const cc_string* str); /* Calls all registered callbacks for an event which has raw pointer movement arguments. */ void Event_RaiseRawMove(struct Event_RawMove* handlers, float xDelta, float yDelta); +/* Calls all registered callbacks for an event which has pad axis arguments. */ +void Event_RaisePadAxis(struct Event_PadAxis* handlers, int port, int axis, float x, float y); /* Calls all registered callbacks for an event which has a channel and a 64 byte data argument. */ void Event_RaisePluginMessage(struct Event_PluginMessage* handlers, cc_uint8 channel, cc_uint8* data); @@ -194,7 +202,7 @@ CC_VAR extern struct _PointerEventsList { } PointerEvents; CC_VAR extern struct _ControllerEventsList { - struct Event_RawMove RawMoved; /* Raw analog controller movement (Arg is delta) */ + struct Event_PadAxis AxisUpdate; /* Raw analog controller movement */ } ControllerEvents; CC_VAR extern struct _NetEventsList { diff --git a/src/ExtMath.c b/src/ExtMath.c index 5081d706f..aa10e9791 100644 --- a/src/ExtMath.c +++ b/src/ExtMath.c @@ -4,10 +4,37 @@ /* For abs(x) function */ #include -#ifndef __GNUC__ -#include -float Math_AbsF(float x) { return fabsf(x); /* MSVC intrinsic */ } -float Math_SqrtF(float x) { return sqrtf(x); /* MSVC intrinsic */ } +/* Sega saturn is missing these intrinsics */ +#ifdef CC_BUILD_SATURN +#include +extern int32_t fix16_sqrt(int32_t value); +static int abs(int x) { return x < 0 ? -x : x; } + +float sqrtf(float x) { + int32_t fp_x = (int32_t)(x * (1 << 16)); + fp_x = fix16_sqrt(fp_x); + return (float)fp_x / (1 << 16); + } +#endif + + +#if defined CC_BUILD_PS1 + /* PS1 is missing these intrinsics */ + #include + float Math_AbsF(float x) { return __builtin_fabsf(x); } + + float Math_SqrtF(float x) { + int fp_x = (int)(x * (1 << 12)); + fp_x = SquareRoot12(fp_x); + return (float)fp_x / (1 << 12); + } +#elif defined __GNUC__ + /* Defined in .h using builtins */ +#else + #include + + float Math_AbsF(float x) { return fabsf(x); /* MSVC intrinsic */ } + float Math_SqrtF(float x) { return sqrtf(x); /* MSVC intrinsic */ } #endif float Math_Mod1(float x) { return x - (int)x; /* fmodf(x, 1); */ } @@ -82,7 +109,7 @@ cc_bool Math_IsPowOf2(int value) { #define RND_MASK ((1ULL << 48) - 1) void Random_SeedFromCurrentTime(RNGState* rnd) { - TimeMS now = DateTime_CurrentUTC_MS(); + cc_uint64 now = Stopwatch_Measure(); Random_Seed(rnd, (int)now); } @@ -121,7 +148,6 @@ float Random_Float(RNGState* seed) { *--------------------------------------------------Transcendental functions-----------------------------------------------* *#########################################################################################################################*/ static const double SQRT2 = 1.4142135623730950488016887242096980785696718753769; -static const double LOGE2 = 0.6931471805599453094172321214581765680755001343602; #ifdef CC_BUILD_DREAMCAST #include @@ -405,10 +431,11 @@ double Math_Atan2(double x, double y) { return Atan(y / x) + PI; return Atan(y / x) - PI; } - if (y > 0) - return PI / 2.0; - if (y < 0) - return -PI / 2.0; + + /* x = 0 case */ + if (y > 0) return PI / 2.0; + if (y < 0) return -PI / 2.0; + return DBL_NAN; } @@ -562,14 +589,4 @@ double Math_Log2(double x) { return exponent + Log2Stage1(doi.d); } -#endif - -/* Uses the property that - * log_e(x) = log_2(x) * log_e(2). - * - * Associated math function: log_e(x) - * Allowed input range: anything - */ -double Math_Log(double x) { - return Math_Log2(x) * LOGE2; -} +#endif \ No newline at end of file diff --git a/src/ExtMath.h b/src/ExtMath.h index a176d5551..f02db6399 100644 --- a/src/ExtMath.h +++ b/src/ExtMath.h @@ -14,7 +14,7 @@ #define Math_Deg2Packed(x) ((cc_uint8)((x) * 256.0f / 360.0f)) #define Math_Packed2Deg(x) ((x) * 360.0f / 256.0f) -#ifdef __GNUC__ +#if defined __GNUC__ && !defined CC_PLAT_PS1 /* fabsf/sqrtf are single intrinsic instructions in gcc/clang */ /* (sqrtf is only when -fno-math-errno though) */ #define Math_AbsF(x) __builtin_fabsf(x) @@ -33,14 +33,11 @@ float Math_SinF(float x); float Math_CosF(float x); double Math_Atan2(double x, double y); -/* Computes loge(x). Can also be used to approximate logy(x). */ -/* e.g. for log3(x), use: Math_Log(x)/log(3) */ -double Math_Log(double x); -/* Computes log2(x). Can also be used to approximate log2(x). */ -/* e.g. for log3(x), use: Math_Log2(x)/log2(3) */ +/* Computes log2(x). Can also be used to approximate log_y(x). */ +/* e.g. for log3(x), use: log2(x)/log2(3) */ double Math_Log2(double x); /* Computes 2^x. Can also be used to approximate y^x. */ -/* e.g. for 3^x, use: Math_Exp2(log2(3)*x) */ +/* e.g. for 3^x, use: exp2(log2(3)*x) */ double Math_Exp2(double x); int Math_Floor(float value); diff --git a/src/Formats.c b/src/Formats.c index 870d6e5a0..af16b4ba1 100644 --- a/src/Formats.c +++ b/src/Formats.c @@ -16,8 +16,9 @@ #include "Chat.h" #include "TexturePack.h" #include "Utils.h" -#include "Lighting.h" -static cc_bool calcDefaultSpawn; + +#ifdef CC_BUILD_FILESYSTEM +static struct LocationUpdate* spawn_point; static struct MapImporter* imp_head; static struct MapImporter* imp_tail; @@ -62,12 +63,13 @@ struct MapImporter* MapImporter_Find(const cc_string* path) { cc_result Map_LoadFrom(const cc_string* path) { cc_string relPath, fileName, fileExt; + struct LocationUpdate update = { 0 }; struct MapImporter* imp; struct Stream stream; cc_result res; Game_Reset(); - calcDefaultSpawn = false; + spawn_point = &update; res = Stream_OpenFile(&stream, path); if (res) { Logger_SysWarn2(res, "opening", path); return res; } @@ -83,8 +85,8 @@ cc_result Map_LoadFrom(const cc_string* path) { if (res) Logger_SysWarn2(res, "decoding", path); World_SetNewMap(World.Blocks, World.Width, World.Height, World.Length); - if (calcDefaultSpawn) LocalPlayer_CalcDefaultSpawn(); - LocalPlayer_MoveToSpawn(); + if (!spawn_point) LocalPlayer_CalcDefaultSpawn(Entities.CurPlayer, &update); + LocalPlayers_MoveToSpawn(&update); relPath = *path; Utils_UNSAFE_GetFilename(&relPath); @@ -183,7 +185,6 @@ static cc_result Lvl_Load(struct Stream* stream) { cc_result res; int i; - struct LocalPlayer* p = &LocalPlayer_Instance; struct Stream compStream; struct InflateState state; Inflate_MakeStream2(&compStream, &state, stream); @@ -196,11 +197,12 @@ static cc_result Lvl_Load(struct Stream* stream) { World.Length = Stream_GetU16_LE(&header[4]); World.Height = Stream_GetU16_LE(&header[6]); - p->Spawn.x = Stream_GetU16_LE(&header[8]); - p->Spawn.z = Stream_GetU16_LE(&header[10]); - p->Spawn.y = Stream_GetU16_LE(&header[12]); - p->SpawnYaw = Math_Packed2Deg(header[14]); - p->SpawnPitch = Math_Packed2Deg(header[15]); + spawn_point->flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; + spawn_point->pos.x = Stream_GetU16_LE(&header[8]); + spawn_point->pos.z = Stream_GetU16_LE(&header[10]); + spawn_point->pos.y = Stream_GetU16_LE(&header[12]); + spawn_point->yaw = Math_Packed2Deg(header[14]); + spawn_point->pitch = Math_Packed2Deg(header[15]); /* (2) pervisit, perbuild permissions */ if ((res = Map_ReadBlocks(&compStream))) return res; @@ -270,7 +272,6 @@ static cc_result Fcm_Load(struct Stream* stream) { cc_result res; int i, count; - struct LocalPlayer* p = &LocalPlayer_Instance; struct Stream compStream; struct InflateState state; Inflate_MakeStream2(&compStream, &state, stream); @@ -283,11 +284,12 @@ static cc_result Fcm_Load(struct Stream* stream) { World.Height = Stream_GetU16_LE(&header[7]); World.Length = Stream_GetU16_LE(&header[9]); - p->Spawn.x = ((int)Stream_GetU32_LE(&header[11])) / 32.0f; - p->Spawn.y = ((int)Stream_GetU32_LE(&header[15])) / 32.0f; - p->Spawn.z = ((int)Stream_GetU32_LE(&header[19])) / 32.0f; - p->SpawnYaw = Math_Packed2Deg(header[23]); - p->SpawnPitch = Math_Packed2Deg(header[24]); + spawn_point->flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; + spawn_point->pos.x = ((int)Stream_GetU32_LE(&header[11])) / 32.0f; + spawn_point->pos.y = ((int)Stream_GetU32_LE(&header[15])) / 32.0f; + spawn_point->pos.z = ((int)Stream_GetU32_LE(&header[19])) / 32.0f; + spawn_point->yaw = Math_Packed2Deg(header[23]); + spawn_point->pitch = Math_Packed2Deg(header[24]); /* header[25] (4) date modified */ /* header[29] (4) date created */ @@ -676,19 +678,18 @@ static void Cw_Callback_1(struct NbtTag* tag) { } static void Cw_Callback_2(struct NbtTag* tag) { - struct LocalPlayer* p = &LocalPlayer_Instance; - if (IsTag(tag->parent, "MapGenerator")) { if (IsTag(tag, "Seed")) { World.Seed = NbtTag_I32(tag); return; } return; } if (!IsTag(tag->parent, "Spawn")) return; + spawn_point->flags = LU_HAS_POS | LU_HAS_YAW | LU_HAS_PITCH; - if (IsTag(tag, "X")) { p->Spawn.x = NbtTag_I16(tag); return; } - if (IsTag(tag, "Y")) { p->Spawn.y = NbtTag_I16(tag); return; } - if (IsTag(tag, "Z")) { p->Spawn.z = NbtTag_I16(tag); return; } - if (IsTag(tag, "H")) { p->SpawnYaw = Math_Packed2Deg(NbtTag_U8(tag)); return; } - if (IsTag(tag, "P")) { p->SpawnPitch = Math_Packed2Deg(NbtTag_U8(tag)); return; } + if (IsTag(tag, "X")) { spawn_point->pos.x = NbtTag_I16(tag); return; } + if (IsTag(tag, "Y")) { spawn_point->pos.y = NbtTag_I16(tag); return; } + if (IsTag(tag, "Z")) { spawn_point->pos.z = NbtTag_I16(tag); return; } + if (IsTag(tag, "H")) { spawn_point->yaw = Math_Packed2Deg(NbtTag_U8(tag)); return; } + if (IsTag(tag, "P")) { spawn_point->pitch = Math_Packed2Deg(NbtTag_U8(tag)); return; } } static BlockID cw_curID; @@ -701,7 +702,7 @@ static PackedCol Cw_ParseColor(PackedCol defValue) { static void Cw_Callback_4(struct NbtTag* tag) { BlockID id = cw_curID; - struct LocalPlayer* p = &LocalPlayer_Instance; + struct LocalPlayer* p = &LocalPlayer_Instances[0]; if (!IsTag(tag->parent->parent, "CPE")) return; if (!IsTag(tag->parent->parent->parent, "Metadata")) return; @@ -1237,7 +1238,7 @@ Classic 0.15 to Classic 0.30: static void Dat_Format0And1(void) { /* Formats 0 and 1 don't store spawn position, so use default of map centre */ - calcDefaultSpawn = true; + spawn_point = NULL; /* Similiar env to how it appears in preclassic - 0.13 classic client */ Env.CloudsHeight = -30000; @@ -1285,7 +1286,6 @@ static cc_result Dat_LoadFormat1(struct Stream* stream) { } static cc_result Dat_LoadFormat2(struct Stream* stream) { - struct LocalPlayer* p = &LocalPlayer_Instance; struct JClassDesc classes[CLASS_CAPACITY]; cc_uint8 header[2 + 2]; struct JUnion obj; @@ -1325,11 +1325,14 @@ static cc_result Dat_LoadFormat2(struct Stream* stream) { World.Blocks = field->Value.Array.Ptr; World.Volume = field->Value.Array.Size; } else if (String_CaselessEqualsConst(&fieldName, "xSpawn")) { - p->Spawn.x = (float)Java_I32(field); + spawn_point->pos.x = (float)Java_I32(field); + spawn_point->flags = LU_HAS_POS; } else if (String_CaselessEqualsConst(&fieldName, "ySpawn")) { - p->Spawn.y = (float)Java_I32(field); + spawn_point->pos.y = (float)Java_I32(field); + spawn_point->flags = LU_HAS_POS; } else if (String_CaselessEqualsConst(&fieldName, "zSpawn")) { - p->Spawn.z = (float)Java_I32(field); + spawn_point->pos.z = (float)Java_I32(field); + spawn_point->flags = LU_HAS_POS; } } return 0; @@ -1446,11 +1449,12 @@ static void MCLevel_Callback_3(struct NbtTag* tag) { struct NbtTag* field = tag->parent; if (IsTag(group, "Map") && IsTag(field, "spawn")) { - cc_int16 value = NbtTag_I16(tag); + cc_int16 value = NbtTag_I16(tag); + spawn_point->flags = LU_HAS_POS; - if (tag->listIndex == 0) LocalPlayer_Instance.Spawn.x = value; - if (tag->listIndex == 1) LocalPlayer_Instance.Spawn.y = value - 1.0f; - if (tag->listIndex == 2) LocalPlayer_Instance.Spawn.z = value; + if (tag->listIndex == 0) spawn_point->pos.x = value; + if (tag->listIndex == 1) spawn_point->pos.y = value - 1.0f; + if (tag->listIndex == 2) spawn_point->pos.z = value; } } @@ -1567,9 +1571,9 @@ static cc_result Cw_WriteBockDef(struct Stream* stream, int b) { } cc_result Cw_Save(struct Stream* stream) { + struct LocalPlayer* p = Entities.CurPlayer; cc_uint8 buffer[2048]; cc_uint8* cur; - struct LocalPlayer* p = &LocalPlayer_Instance; cc_result res; int b; @@ -1617,7 +1621,7 @@ cc_result Cw_Save(struct Stream* stream) { { cur = Nbt_WriteDict(cur, "ClickDistance"); { - cur = Nbt_WriteUInt16(cur, "Distance", (cc_uint16)(LocalPlayer_Instance.ReachDistance * 32)); + cur = Nbt_WriteUInt16(cur, "Distance", (cc_uint16)(p->ReachDistance * 32)); } *cur++ = NBT_END; cur = Nbt_WriteDict(cur, "EnvWeatherType"); @@ -1734,9 +1738,9 @@ static const struct JField { { JFIELD_I32, false, "width", &World.Width }, { JFIELD_I32, false, "depth", &World.Height }, { JFIELD_I32, false, "height", &World.Length }, - { JFIELD_I32, true, "xSpawn", &LocalPlayer_Instance.Base.Position.x }, - { JFIELD_I32, true, "ySpawn", &LocalPlayer_Instance.Base.Position.y }, - { JFIELD_I32, true, "zSpawn", &LocalPlayer_Instance.Base.Position.z }, + { JFIELD_I32, true, "xSpawn", &LocalPlayer_Instances[0].Base.Position.x }, + { JFIELD_I32, true, "ySpawn", &LocalPlayer_Instances[0].Base.Position.y }, + { JFIELD_I32, true, "zSpawn", &LocalPlayer_Instances[0].Base.Position.z }, { JFIELD_ARRAY,0, "blocks" } /* TODO classic only blocks */ }; @@ -1874,8 +1878,20 @@ static void OnInit(void) { static void OnFree(void) { imp_head = NULL; } +#else +/* No point including map format code when can't save/load maps anyways */ +struct MapImporter* MapImporter_Find(const cc_string* path) { return NULL; } +cc_result Map_LoadFrom(const cc_string* path) { return ERR_NOT_SUPPORTED; } + +cc_result Cw_Save(struct Stream* stream) { return ERR_NOT_SUPPORTED; } +cc_result Dat_Save(struct Stream* stream) { return ERR_NOT_SUPPORTED; } +cc_result Schematic_Save(struct Stream* stream) { return ERR_NOT_SUPPORTED; } + +static void OnInit(void) { } +static void OnFree(void) { } +#endif struct IGameComponent Formats_Component = { OnInit, /* Init */ OnFree /* Free */ -}; \ No newline at end of file +}; diff --git a/src/Funcs.h b/src/Funcs.h index 5d1222c50..1edd33549 100644 --- a/src/Funcs.h +++ b/src/Funcs.h @@ -39,4 +39,14 @@ if (!head) { head = item; } else { tail->next = item; }\ tail = item;\ item->next = NULL; +#define LinkedList_Remove(item, cur, head, tail)\ +cur = head; \ +if (head == item) head = item->next;\ +\ +while (cur) {\ + if (cur->next == item) cur->next = item->next; \ + \ + tail = cur;\ + cur = cur->next;\ +} #endif diff --git a/src/Game.c b/src/Game.c index f3ae75728..3f7d161a8 100644 --- a/src/Game.c +++ b/src/Game.c @@ -57,6 +57,7 @@ static cc_bool gameRunning; cc_bool Game_ClassicMode, Game_ClassicHacks; cc_bool Game_AllowCustomBlocks; cc_bool Game_AllowServerTextures; +cc_bool Game_Anaglyph3D; cc_bool Game_ViewBobbing, Game_HideGui; cc_bool Game_BreakableLiquids, Game_ScreenshotRequested; @@ -64,8 +65,11 @@ struct GameVersion Game_Version; static char usernameBuffer[STRING_SIZE]; static char mppassBuffer[STRING_SIZE]; -cc_string Game_Username = String_FromArray(usernameBuffer); -cc_string Game_Mppass = String_FromArray(mppassBuffer); +cc_string Game_Username = String_FromArray(usernameBuffer); +cc_string Game_Mppass = String_FromArray(mppassBuffer); +#ifdef CC_BUILD_SPLITSCREEN +int Game_NumLocalPlayers = 1; +#endif const char* const FpsLimit_Names[FPS_LIMIT_COUNT] = { "LimitVSync", "Limit30FPS", "Limit60FPS", "Limit120FPS", "Limit144FPS", "LimitNone", @@ -306,10 +310,13 @@ static void HandleInactiveChanged(void* obj) { if (Window_Main.Inactive) { Chat_AddOf(&Gfx_LowPerfMessage, MSG_TYPE_EXTRASTATUS_2); Gfx_SetFpsLimit(false, 1000 / 1.0f); + Gfx.ReducedPerfMode = true; } else { Chat_AddOf(&String_Empty, MSG_TYPE_EXTRASTATUS_2); Game_SetFpsLimit(Game_FpsLimit); - Chat_AddRaw(LOWPERF_EXIT_MESSAGE); + + Gfx.ReducedPerfMode = false; + Gfx.ReducedPerfModeCooldown = 2; } #ifdef CC_BUILD_WEB @@ -327,16 +334,18 @@ static void Game_WarnFunc(const cc_string* msg) { } static void LoadOptions(void) { - Game_ClassicMode = Options_GetBool(OPT_CLASSIC_MODE, false); - Game_ClassicHacks = Options_GetBool(OPT_CLASSIC_HACKS, false); - Game_AllowCustomBlocks = Options_GetBool(OPT_CUSTOM_BLOCKS, true); - Game_SimpleArmsAnim = Options_GetBool(OPT_SIMPLE_ARMS_ANIM, false); - Game_ViewBobbing = Options_GetBool(OPT_VIEW_BOBBING, true); + Game_ClassicMode = Options_GetBool(OPT_CLASSIC_MODE, false); + Game_ClassicHacks = Options_GetBool(OPT_CLASSIC_HACKS, false); + Game_Anaglyph3D = Options_GetBool(OPT_ANAGLYPH3D, false); + Game_ViewBobbing = Options_GetBool(OPT_VIEW_BOBBING, true); + + Game_AllowCustomBlocks = !Game_ClassicMode && Options_GetBool(OPT_CUSTOM_BLOCKS, true); + Game_SimpleArmsAnim = !Game_ClassicMode && Options_GetBool(OPT_SIMPLE_ARMS_ANIM, false); + Game_BreakableLiquids = !Game_ClassicMode && Options_GetBool(OPT_MODIFIABLE_LIQUIDS, false); + Game_AllowServerTextures = !Game_ClassicMode && Options_GetBool(OPT_SERVER_TEXTURES, true); Game_ViewDistance = Options_GetInt(OPT_VIEW_DISTANCE, 8, 4096, DEFAULT_VIEWDIST); Game_UserViewDistance = Game_ViewDistance; - Game_BreakableLiquids = !Game_ClassicMode && Options_GetBool(OPT_MODIFIABLE_LIQUIDS, false); - Game_AllowServerTextures = Options_GetBool(OPT_SERVER_TEXTURES, true); /* TODO: Do we need to support option to skip SSL */ /*cc_bool skipSsl = Options_GetBool("skip-ssl-check", false); if (skipSsl) { @@ -345,9 +354,7 @@ static void LoadOptions(void) { }*/ } -#ifdef CC_BUILD_MINFILES -static void LoadPlugins(void) { } -#else +#ifdef CC_BUILD_PLUGINS static void LoadPlugin(const cc_string* path, void* obj) { void* lib; void* verSym; /* EXPORT int Plugin_ApiVersion = GAME_API_VER; */ @@ -388,6 +395,8 @@ static void LoadPlugins(void) { res = Directory_Enum(&dir, NULL, LoadPlugin); if (res) Logger_SysWarn(res, "enumerating plugins directory"); } +#else +static void LoadPlugins(void) { } #endif static void Game_Free(void* obj); @@ -396,6 +405,7 @@ static void Game_Load(void) { Game_UpdateDimensions(); Game_SetFpsLimit(Options_GetEnum(OPT_FPS_LIMIT, 0, FpsLimit_Names, FPS_LIMIT_COUNT)); Gfx_Create(); + Logger_WarnFunc = Game_WarnFunc; LoadOptions(); GameVersion_Load(); @@ -473,12 +483,14 @@ void Game_SetFpsLimit(int method) { } static void UpdateViewMatrix(void) { - Camera.Active->GetView(&Gfx.View); + Camera.Active->GetView(Entities.CurPlayer, &Gfx.View); FrustumCulling_CalcFrustumEquations(&Gfx.Projection, &Gfx.View); } -static void Game_Render3D(double delta, float t) { +static void Render3DFrame(float delta, float t) { Vec3 pos; + Gfx_LoadMatrix(MATRIX_PROJECTION, &Gfx.Projection); + Gfx_LoadMatrix(MATRIX_VIEW, &Gfx.View); if (EnvRenderer_ShouldRenderSkybox()) EnvRenderer_RenderSkybox(); AxisLinesRenderer_Render(); @@ -486,7 +498,6 @@ static void Game_Render3D(double delta, float t) { EntityNames_Render(); Particles_Render(t); - Camera.Active->GetPickedBlock(&Game_SelectedPos); /* TODO: only pick when necessary */ EnvRenderer_RenderSky(); EnvRenderer_RenderClouds(); @@ -495,7 +506,7 @@ static void Game_Render3D(double delta, float t) { EnvRenderer_RenderMapSides(); EntityShadows_Render(); - if (Game_SelectedPos.Valid && !Game_HideGui) { + if (Game_SelectedPos.valid && !Game_HideGui) { SelOutlineRenderer_Render(&Game_SelectedPos, true); } @@ -511,17 +522,28 @@ static void Game_Render3D(double delta, float t) { /* Need to render again over top of translucent block, as the selection outline */ /* is drawn without writing to the depth buffer */ - if (Game_SelectedPos.Valid && !Game_HideGui && Blocks.Draw[Game_SelectedPos.block] == DRAW_TRANSLUCENT) { + if (Game_SelectedPos.valid && !Game_HideGui && Blocks.Draw[Game_SelectedPos.block] == DRAW_TRANSLUCENT) { SelOutlineRenderer_Render(&Game_SelectedPos, false); } Selections_Render(); EntityNames_RenderHovered(); - Camera_KeyLookUpdate(delta); - InputHandler_Tick(); if (!Game_HideGui) HeldBlockRenderer_Render(delta); } +static void Render3D_Anaglyph(float delta, float t) { + struct Matrix proj = Gfx.Projection; + struct Matrix view = Gfx.View; + + Gfx_Set3DLeft(&proj, &view); + Render3DFrame(delta, t); + + Gfx_Set3DRight(&proj, &view); + Render3DFrame(delta, t); + + Gfx_End3D(&proj, &view); +} + static void PerformScheduledTasks(double time) { struct ScheduledTask* task; int i; @@ -581,7 +603,50 @@ void Game_TakeScreenshot(void) { #endif } -static void Game_RenderFrame(double delta) { +static CC_INLINE void Game_DrawFrame(float delta, float t) { + UpdateViewMatrix(); + + if (!Gui_GetBlocksWorld()) { + Camera.Active->GetPickedBlock(Entities.CurPlayer, &Game_SelectedPos); /* TODO: only pick when necessary */ + Camera_KeyLookUpdate(delta); + InputHandler_Tick(); + + if (Game_Anaglyph3D) { + Render3D_Anaglyph(delta, t); + } else { + Render3DFrame(delta, t); + } + } else { + RayTracer_SetInvalid(&Game_SelectedPos); + } + + Gfx_Begin2D(Game.Width, Game.Height); + Gui_RenderGui(delta); + OnscreenKeyboard_Draw3D(); +/* TODO find a better solution than this */ +#ifdef CC_BUILD_3DS + if (Game_Anaglyph3D) { + extern void Gfx_SetTopRight(void); + Gfx_SetTopRight(); + Gui_RenderGui(delta); + } +#endif + Gfx_End2D(); +} + +#ifdef CC_BUILD_SPLITSCREEN +static void DrawSplitscreen(float delta, float t, int i, int x, int y, int w, int h) { + Gfx_SetViewport(x, y, w, h); + + Entities.CurPlayer = &LocalPlayer_Instances[i]; + LocalPlayer_SetInterpPosition(Entities.CurPlayer, t); + Camera.CurrentPos = Camera.Active->GetPosition(Entities.CurPlayer, t); + + Game_DrawFrame(delta, t); +} +#endif + +static CC_INLINE void Game_RenderFrame(double delta) { struct ScheduledTask entTask; float t; @@ -605,7 +670,9 @@ static void Game_RenderFrame(double delta) { Game.Time += delta; Game_Vertices = 0; - Camera.Active->UpdateMouse(delta); + if (Input.Sources & INPUT_SOURCE_GAMEPAD) Gamepad_Tick(delta); + Camera.Active->UpdateMouse(Entities.CurPlayer, delta); + if (!Window_Main.Focused && !Gui.InputGrab) Gui_ShowPauseMenu(); if (KeyBind_IsPressed(KEYBIND_ZOOM_SCROLL) && !Gui.InputGrab) { @@ -615,29 +682,40 @@ static void Game_RenderFrame(double delta) { PerformScheduledTasks(delta); entTask = tasks[entTaskI]; t = (float)(entTask.accumulator / entTask.interval); - LocalPlayer_SetInterpPosition(t); + LocalPlayer_SetInterpPosition(Entities.CurPlayer, t); - Camera.CurrentPos = Camera.Active->GetPosition(t); + Camera.CurrentPos = Camera.Active->GetPosition(Entities.CurPlayer, t); /* NOTE: EnvRenderer_UpdateFog also also sets clear color */ EnvRenderer_UpdateFog(); - UpdateViewMatrix(); + AudioBackend_Tick(); /* TODO: Not calling Gfx_EndFrame doesn't work with Direct3D9 */ if (Window_Main.Inactive) return; - Gfx_Clear(); - - Gfx_LoadMatrix(MATRIX_PROJECTION, &Gfx.Projection); - Gfx_LoadMatrix(MATRIX_VIEW, &Gfx.View); - - if (!Gui_GetBlocksWorld()) { - Game_Render3D(delta, t); - } else { - RayTracer_SetInvalid(&Game_SelectedPos); + Gfx_ClearBuffers(GFX_BUFFER_COLOR | GFX_BUFFER_DEPTH); + +#ifdef CC_BUILD_SPLITSCREEN + switch (Game_NumLocalPlayers) { + case 1: + Game_DrawFrame(delta, t); break; + case 2: + DrawSplitscreen(delta, t, 0, 0, 0, Game.Width, Game.Height / 2); + DrawSplitscreen(delta, t, 1, 0, Game.Height / 2, Game.Width, Game.Height / 2); + break; + case 3: + DrawSplitscreen(delta, t, 0, 0, 0, Game.Width , Game.Height / 2); + DrawSplitscreen(delta, t, 1, 0, Game.Height / 2, Game.Width / 2, Game.Height / 2); + DrawSplitscreen(delta, t, 2, Game.Width / 2, Game.Height / 2, Game.Width / 2, Game.Height / 2); + break; + case 4: + DrawSplitscreen(delta, t, 0, 0, 0, Game.Width / 2, Game.Height / 2); + DrawSplitscreen(delta, t, 1, Game.Width / 2, 0, Game.Width / 2, Game.Height / 2); + DrawSplitscreen(delta, t, 2, 0, Game.Height / 2, Game.Width / 2, Game.Height / 2); + DrawSplitscreen(delta, t, 3, Game.Width / 2, Game.Height / 2, Game.Width / 2, Game.Height / 2); + break; } - - Gfx_Begin2D(Game.Width, Game.Height); - Gui_RenderGui(delta); - Gfx_End2D(); +#else + Game_DrawFrame(delta, t); +#endif if (Game_ScreenshotRequested) Game_TakeScreenshot(); Gfx_EndFrame(); diff --git a/src/Game.h b/src/Game.h index db22d993a..ffefd1e02 100644 --- a/src/Game.h +++ b/src/Game.h @@ -22,12 +22,20 @@ extern struct RayTracer Game_SelectedPos; extern cc_bool Game_UseCPEBlocks; extern cc_string Game_Username; -extern cc_string Game_Mppass; +extern cc_string Game_Mppass; -#ifdef CC_BUILD_N64 -#define DEFAULT_VIEWDIST 20 +#ifdef CC_BUILD_SPLITSCREEN +extern int Game_NumLocalPlayers; #else -#define DEFAULT_VIEWDIST 512 +#define Game_NumLocalPlayers 1 +#endif + +#if defined CC_BUILD_N64 + #define DEFAULT_VIEWDIST 20 +#elif defined CC_BUILD_NDS || defined CC_BUILD_PS1 + #define DEFAULT_VIEWDIST 192 +#else + #define DEFAULT_VIEWDIST 512 #endif #define DEFAULT_MAX_VIEWDIST 32768 @@ -46,6 +54,7 @@ extern cc_bool Game_ClassicHacks; extern cc_bool Game_AllowCustomBlocks; extern cc_bool Game_AllowServerTextures; +extern cc_bool Game_Anaglyph3D; extern cc_bool Game_ViewBobbing; extern cc_bool Game_BreakableLiquids; /* Whether a screenshot should be taken at the end of this frame */ diff --git a/src/Generator.c b/src/Generator.c index df7fffb71..94b0029fa 100644 --- a/src/Generator.c +++ b/src/Generator.c @@ -69,8 +69,8 @@ static void Gen_DoGen(void) { } static void Gen_Run(void) { - void* thread = Thread_Create(Gen_DoGen); - Thread_Start2(thread, Gen_DoGen); + void* thread; + Thread_Run(&thread, Gen_DoGen, 128 * 1024, "Map gen"); Thread_Detach(thread); } @@ -671,7 +671,7 @@ static void NotchyGen_PlantTrees(void) { treeX += Random_Next(&rnd, 6) - Random_Next(&rnd, 6); treeZ += Random_Next(&rnd, 6) - Random_Next(&rnd, 6); - if (!World_ContainsXZ(treeX, treeZ) || Random_Float(&rnd) >= 0.25) continue; + if (!World_ContainsXZ(treeX, treeZ) || Random_Float(&rnd) >= 0.25f) continue; treeY = heightmap[treeZ * World.Width + treeX] + 1; if (treeY >= World.Height) continue; treeHeight = 5 + Random_Next(&rnd, 3); diff --git a/src/Graphics.h b/src/Graphics.h index 04d1fbc05..29b5487d1 100644 --- a/src/Graphics.h +++ b/src/Graphics.h @@ -61,6 +61,11 @@ CC_VAR extern struct _GfxData { /* Maximum total size in pixels a low resolution texture can consist of */ /* NOTE: Not all graphics backends specify a value for this */ int MaxLowResTexSize; + /* Minimum dimensions in pixels that a texture must be */ + /* NOTE: Most graphics backends do not use this */ + int MinTexWidth, MinTexHeight; + cc_bool ReducedPerfMode; + cc_uint8 ReducedPerfModeCooldown; } Gfx; extern GfxResourceID Gfx_defaultIb; @@ -70,6 +75,11 @@ extern const cc_string Gfx_LowPerfMessage; #define GFX_MAX_INDICES (65536 / 4 * 6) #define GFX_MAX_VERTICES 65536 +typedef enum GfxBuffers_ { + GFX_BUFFER_COLOR = 1, + GFX_BUFFER_DEPTH = 2 +} GfxBuffers; + /* Texture should persist across gfx context loss (if backend supports ManagedTextures) */ #define TEXTURE_FLAG_MANAGED 0x01 /* Texture should allow updating via Gfx_UpdateTexture */ @@ -79,8 +89,6 @@ extern const cc_string Gfx_LowPerfMessage; /* Texture can fallback to 16 bpp when necessary (most backends don't do this) */ #define TEXTURE_FLAG_LOWRES 0x08 -#define LOWPERF_EXIT_MESSAGE "&eExited reduced performance mode" - void Gfx_RecreateTexture(GfxResourceID* tex, struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps); void* Gfx_RecreateAndLockVb(GfxResourceID* vb, VertexFormat fmt, int count); @@ -90,6 +98,7 @@ cc_bool Gfx_CheckTextureSize(int width, int height, cc_uint8 flags); /* NOTE: Only set mipmaps to true if Gfx_Mipmaps is also true, because whether textures use mipmapping may be either a per-texture or global state depending on the backend */ CC_API GfxResourceID Gfx_CreateTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps); +GfxResourceID Gfx_CreateTexture2(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps); /* Updates a region of the given texture. (and mipmapped regions if mipmaps) */ CC_API void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps); /* Updates a region of the given texture. (and mipmapped regions if mipmaps) */ @@ -132,20 +141,26 @@ CC_API void Gfx_SetAlphaBlending(cc_bool enabled); /* Sets whether blending between the alpha components of texture and vertex colour is performed */ CC_API void Gfx_SetAlphaArgBlend(cc_bool enabled); -/* Clears the colour and depth buffer to default */ -CC_API void Gfx_Clear(void); +/* Clears the given rendering buffer(s) to default. */ +/* buffers can be either GFX_BUFFER_COLOR or GFX_BUFFER_DEPTH, or both */ +CC_API void Gfx_ClearBuffers(GfxBuffers buffers); /* Sets the colour that the colour buffer is cleared to */ -CC_API void Gfx_ClearCol(PackedCol col); +CC_API void Gfx_ClearColor(PackedCol color); /* Sets whether pixels may be discard based on z/depth */ CC_API void Gfx_SetDepthTest(cc_bool enabled); -/* Sets whether R/G/B/A of pixels are actually written to the colour buffer channels */ -CC_API void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a); /* Sets whether z/depth of pixels is actually written to the depth buffer */ CC_API void Gfx_SetDepthWrite(cc_bool enabled); +/* Sets whether R/G/B/A of pixels are actually written to the colour buffer channels */ +CC_API void Gfx_SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a); /* Sets whether the game should only write output to depth buffer */ -/* NOTE: Implicitly calls Gfx_SetColWriteMask */ +/* NOTE: Implicitly calls Gfx_SetColorWrite */ CC_API void Gfx_DepthOnlyRendering(cc_bool depthOnly); +/* Anaglyph 3D rendering support */ +void Gfx_Set3DLeft( struct Matrix* proj, struct Matrix* view); +void Gfx_Set3DRight(struct Matrix* proj, struct Matrix* view); +void Gfx_End3D( struct Matrix* proj, struct Matrix* view); + /* Callback function to initialise/fill out the contents of an index buffer */ typedef void (*Gfx_FillIBFunc)(cc_uint16* indices, int count, void* obj); /* Creates a new index buffer and fills out its contents */ @@ -230,14 +245,16 @@ void Gfx_EndFrame(void); /* Sets whether to synchronise with monitor refresh to avoid tearing, and maximum frame rate */ /* NOTE: VSync setting may be unsupported or just ignored */ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMillis); -/* Updates state when the window's dimensions have changed */ -/* NOTE: This may require recreating the context depending on the backend */ -void Gfx_OnWindowResize(void); /* Gets information about the user's GPU and current backend state */ /* Backend state may include depth buffer bits, free memory, etc */ /* NOTE: Each line is separated by \n */ void Gfx_GetApiInfo(cc_string* info); +/* Updates state when the window's dimensions have changed */ +/* NOTE: This may require recreating the context depending on the backend */ +void Gfx_OnWindowResize(void); +void Gfx_SetViewport(int x, int y, int w, int h); + enum Screen3DS { TOP_SCREEN, BOTTOM_SCREEN }; #ifdef CC_BUILD_DUALSCREEN /* Selects which screen on the 3DS to render to */ @@ -280,13 +297,13 @@ void Gfx_RestoreAlphaState(cc_uint8 draw); /* Statically initialises the texture coordinate corners of this texture */ #define Tex_UV(u1,v1, u2,v2) { u1,v1,u2,v2 } /* Sets the position and dimensions of this texture */ -#define Tex_SetRect(tex, xVal,yVal, width, height) tex.x = xVal; tex.y = yVal; tex.Width = width; tex.Height = height; +#define Tex_SetRect(tex, xVal,yVal, wVal, hVal) tex.x = xVal; tex.y = yVal; tex.width = wVal; tex.height = hVal; /* Sets texture coordinate corners of this texture */ /* Useful to only draw a sub-region of the texture's pixels */ -#define Tex_SetUV(tex, u1,v1, u2,v2) tex.uv.U1 = u1; tex.uv.V1 = v1; tex.uv.U2 = u2; tex.uv.V2 = v2; +#define Tex_SetUV(tex, U1,V1, U2,V2) tex.uv.u1 = U1; tex.uv.v1 = V1; tex.uv.u2 = U2; tex.uv.v2 = V2; /* Binds then renders the given texture */ void Texture_Render(const struct Texture* tex); /* Binds then renders the given texture */ void Texture_RenderShaded(const struct Texture* tex, PackedCol shadeColor); -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_3DS.c b/src/Graphics_3DS.c index 0cf5257f0..36c8d53ec 100644 --- a/src/Graphics_3DS.c +++ b/src/Graphics_3DS.c @@ -5,7 +5,8 @@ #include "Logger.h" #include "Window.h" #include <3ds.h> -#include +#define BUFFER_BASE_PADDR OS_VRAM_PADDR // VRAM physical address +#include "../third_party/citro3d.c" // autogenerated from the .v.pica files by Makefile extern const u8 coloured_shbin[]; @@ -24,7 +25,9 @@ extern const u32 offset_shbin_size; static void GPUBuffers_DeleteUnreferenced(void); static void GPUTextures_DeleteUnreferenced(void); -static cc_uint32 frameCounter; +static cc_uint32 frameCounter1; +static PackedCol clear_color; +static cc_bool rendering3D; /*########################################################################################################################* @@ -74,12 +77,12 @@ static void ReloadUniforms(void) { if (!s) return; // NULL if context is lost if (s->uniforms & UNI_MVP_MATRIX) { - C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, s->locations[0], &_mvp); + C3D_FVUnifMtx4x4(s->locations[0], &_mvp); s->uniforms &= ~UNI_MVP_MATRIX; } if (s->uniforms & UNI_TEX_OFFSETS) { - C3D_FVUnifSet(GPU_VERTEX_SHADER, s->locations[1], + C3D_FVUnifSet(s->locations[1], texOffsetX, texOffsetY, 0.0f, 0.0f); s->uniforms &= ~UNI_TEX_OFFSETS; } @@ -106,8 +109,11 @@ static void SwitchProgram(void) { /*########################################################################################################################* *---------------------------------------------------------General---------------------------------------------------------* *#########################################################################################################################*/ +static C3D_RenderTarget topTargetLeft; +static C3D_RenderTarget topTargetRight; +static C3D_RenderTarget bottomTarget; +static cc_bool createdTopTargetRight; static C3D_RenderTarget* topTarget; -static C3D_RenderTarget* bottomTarget; static void AllocShaders(void) { Shader_Alloc(&shaders[0], coloured_shbin, coloured_shbin_size); @@ -129,15 +135,15 @@ static void SetDefaultState(void) { } static void InitCitro3D(void) { - C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE * 4); - topTarget = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetOutput(topTarget, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + C3D_RenderTargetCreate(&topTargetLeft, 240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24); + C3D_RenderTargetSetOutput(&topTargetLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); // Even though the bottom screen is 320 pixels wide, we use 400 here so that the same ortho matrix // can be used for both screens. The output is clipped to the actual screen width, anyway. - bottomTarget = C3D_RenderTargetCreate(240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetOutput(bottomTarget, GFX_BOTTOM, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + C3D_RenderTargetCreate(&bottomTarget, 240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24); + C3D_RenderTargetSetOutput(&bottomTarget, GFX_BOTTOM, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); gfxSetDoubleBuffering(GFX_TOP, true); SetDefaultState(); @@ -147,9 +153,14 @@ static void InitCitro3D(void) { static GfxResourceID white_square; void Gfx_Create(void) { if (!Gfx.Created) InitCitro3D(); + else C3Di_RenderQueueInit(); - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; + + Gfx.MinTexWidth = 8; + Gfx.MinTexHeight = 8; Gfx.Created = true; gfx_vsync = true; @@ -158,6 +169,9 @@ void Gfx_Create(void) { void Gfx_Free(void) { Gfx_FreeState(); + C3Di_RenderQueueExit(); + gfxSet3D(false); + // FreeShaders() // C3D_Fini() } @@ -182,9 +196,57 @@ void Gfx_FreeState(void) { } void Gfx_3DS_SetRenderScreen(enum Screen3DS screen) { - C3D_FrameDrawOn(screen == TOP_SCREEN ? topTarget : bottomTarget); + C3D_FrameDrawOn(screen == TOP_SCREEN ? topTarget : &bottomTarget); } + +/*########################################################################################################################* +*----------------------------------------------------Stereoscopic support-------------------------------------------------* +*#########################################################################################################################*/ +static void Calc3DProjection(int dir, struct Matrix* proj) { + struct Matrix proj_adj = *proj; + + // See mtx_perspstereotilt + float slider = osGet3DSliderState(); + float iod = (slider / 3) * dir; + float shift = iod / (2.0f * 2.0f); + + proj_adj.row3.y = 1.0f * shift * -proj->row1.y; + Gfx.Projection = proj_adj; +} + +void Gfx_Set3DLeft(struct Matrix* proj, struct Matrix* view) { + Calc3DProjection(-1, proj); + rendering3D = true; +} + +void Gfx_Set3DRight(struct Matrix* proj, struct Matrix* view) { + Calc3DProjection(+1, proj); + + if (!createdTopTargetRight) { + C3D_RenderTargetCreate(&topTargetRight, 240, 400, GPU_RB_RGBA8, GPU_RB_DEPTH24); + C3D_RenderTargetSetOutput(&topTargetRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS); + createdTopTargetRight = true; + } + + C3D_RenderTargetClear(&topTargetRight, C3D_CLEAR_ALL, clear_color, 0); + topTarget = &topTargetRight; + C3D_FrameDrawOn(topTarget); +} + +void Gfx_End3D(struct Matrix* proj, struct Matrix* view) { + Gfx.Projection = *proj; + + topTarget = &topTargetLeft; + C3D_FrameDrawOn(topTarget); +} + +void Gfx_SetTopRight(void) { + topTarget = &topTargetRight; + C3D_FrameDrawOn(topTarget); +} + + /*########################################################################################################################* *--------------------------------------------------------GPU Textures-----------------------------------------------------* *#########################################################################################################################*/ @@ -197,10 +259,12 @@ struct GPUTexture { }; static struct GPUTexture* del_textures_head; static struct GPUTexture* del_textures_tail; + struct GPUTexture* GPUTexture_Alloc(void) { struct GPUTexture* tex = Mem_AllocCleared(1, sizeof(struct GPUTexture), "GPU texture"); return tex; } + // can't delete textures until not used in any frames static void GPUTexture_Unref(GfxResourceID* resource) { struct GPUTexture* tex = (struct GPUTexture*)(*resource); @@ -209,10 +273,12 @@ static void GPUTexture_Unref(GfxResourceID* resource) { LinkedList_Append(tex, del_textures_head, del_textures_tail); } + static void GPUTexture_Free(struct GPUTexture* tex) { C3D_TexDelete(&tex->texture); Mem_Free(tex); } + static void GPUTextures_DeleteUnreferenced(void) { if (!del_textures_head) return; @@ -224,7 +290,7 @@ static void GPUTextures_DeleteUnreferenced(void) { { next = tex->next; - if (tex->lastFrame + 4 > frameCounter) { + if (tex->lastFrame + 4 > frameCounter1) { // texture was used within last 4 fames prev = tex; } else { @@ -247,6 +313,38 @@ static void GPUTextures_DeleteUnreferenced(void) { /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ +static bool CreateNativeTexture(C3D_Tex* tex, u32 width, u32 height) { + u32 size = width * height * 4; + //tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); + tex->data = linearAlloc(size); + if (!tex->data) return false; + + tex->width = width; + tex->height = height; + tex->param = GPU_TEXTURE_MODE(GPU_TEX_2D) | + GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST) | + GPU_TEXTURE_WRAP_S(GPU_REPEAT) | GPU_TEXTURE_WRAP_T(GPU_REPEAT); + tex->fmt = GPU_RGBA8; + tex->size = size; + + tex->border = 0; + tex->lodBias = 0; + tex->maxLevel = 0; + tex->minLevel = 0; + return true; +} + +static void TryTransferToVRAM(C3D_Tex* tex) { + return; + // NOTE: the linearFree below results in broken texture. maybe no DMA? + void* vram = vramAlloc(tex->size); + if (!vram) return; + + C3D_SyncTextureCopy((u32*)tex->data, 0, (u32*)vram, 0, tex->size, 8); + linearFree(tex->data); + tex->data = vram; +} + /*static inline cc_uint32 CalcZOrder(cc_uint32 x, cc_uint32 y) { // Simplified "Interleave bits by Binary Magic Numbers" from // http://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious @@ -302,25 +400,20 @@ static void ToMortonTexture(C3D_Tex* tex, int originX, int originY, } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { struct GPUTexture* tex = GPUTexture_Alloc(); - bool success = C3D_TexInit(&tex->texture, bmp->width, bmp->height, GPU_RGBA8); - //if (!success) Logger_Abort("Failed to create 3DS texture"); + bool success = CreateNativeTexture(&tex->texture, bmp->width, bmp->height); + if (!success) return NULL; - ToMortonTexture(&tex->texture, 0, 0, bmp, bmp->width); - C3D_TexSetFilter(&tex->texture, GPU_NEAREST, GPU_NEAREST); - C3D_TexSetWrap(&tex->texture, GPU_REPEAT, GPU_REPEAT); - return tex; + ToMortonTexture(&tex->texture, 0, 0, bmp, rowWidth); + if (!(flags & TEXTURE_FLAG_DYNAMIC)) TryTransferToVRAM(&tex->texture); + return tex; } void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { struct GPUTexture* tex = (struct GPUTexture*)texId; ToMortonTexture(&tex->texture, x, y, part, rowWidth); } - -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} void Gfx_DeleteTexture(GfxResourceID* texId) { GPUTexture_Unref(texId); } @@ -332,7 +425,7 @@ void Gfx_BindTexture(GfxResourceID texId) { if (!texId) texId = white_square; struct GPUTexture* tex = (struct GPUTexture*)texId; - tex->lastFrame = frameCounter; + tex->lastFrame = frameCounter1; C3D_TexBind(0, &tex->texture); } @@ -360,11 +453,11 @@ void Gfx_SetAlphaTest(cc_bool enabled) { void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } -static PackedCol clear_color; -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { // TODO find better way? clear_color = (PackedCol_R(color) << 24) | (PackedCol_G(color) << 16) | (PackedCol_B(color) << 8) | 0xFF; } @@ -384,12 +477,13 @@ void Gfx_SetDepthWrite(cc_bool enabled) { depthWrite = enabled; UpdateWriteState(); } + void Gfx_SetDepthTest(cc_bool enabled) { depthTest = enabled; UpdateWriteState(); } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { int mask = 0; if (r) mask |= GPU_WRITE_RED; if (g) mask |= GPU_WRITE_GREEN; @@ -404,8 +498,34 @@ void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ +static BitmapCol* _3DS_GetRow(struct Bitmap* bmp, int y, void* ctx) { + u8* fb = (u8*)ctx; + // Framebuffer is rotated 90 degrees + int width = bmp->width, height = bmp->height; + + for (int x = 0; x < width; x++) + { + int addr = (height - 1 - y + x * height) * 3; // TODO -1 or not + int b = fb[addr + 0]; + int g = fb[addr + 1]; + int r = fb[addr + 2]; + bmp->scan0[x] = BitmapColor_RGB(r, g, b); + } + return bmp->scan0; +} + cc_result Gfx_TakeScreenshot(struct Stream* output) { - return ERR_NOT_SUPPORTED; + BitmapCol tmp[512]; + u16 width, height; + u8* fb = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &width, &height); + + // Framebuffer is rotated 90 degrees + struct Bitmap bmp; + bmp.scan0 = tmp; + bmp.width = height; + bmp.height = width; + + return Png_Encode(&bmp, output, _3DS_GetRow, false, fb); } void Gfx_GetApiInfo(cc_string* info) { @@ -419,32 +539,44 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { } void Gfx_BeginFrame(void) { - int flags = gfx_vsync ? C3D_FRAME_SYNCDRAW : 0; - C3D_FrameBegin(flags); + rendering3D = false; + // wait for vblank for both screens TODO move to end? + if (gfx_vsync) C3D_FrameSync(); + + C3D_FrameBegin(0); + topTarget = &topTargetLeft; C3D_FrameDrawOn(topTarget); + + extern void C3Di_UpdateContext(void); + C3Di_UpdateContext(); } -void Gfx_Clear(void) { - C3D_RenderTargetClear(topTarget, C3D_CLEAR_ALL, clear_color, 0); - C3D_RenderTargetClear(bottomTarget, C3D_CLEAR_ALL, 0, 0); +void Gfx_ClearBuffers(GfxBuffers buffers) { + int targets = 0; + if (buffers & GFX_BUFFER_COLOR) targets |= C3D_CLEAR_COLOR; + if (buffers & GFX_BUFFER_DEPTH) targets |= C3D_CLEAR_DEPTH; + + C3D_RenderTargetClear(&topTargetLeft, targets, clear_color, 0); + C3D_RenderTargetClear(&bottomTarget, targets, 0, 0); } void Gfx_EndFrame(void) { + gfxSet3D(rendering3D); C3D_FrameEnd(0); //gfxFlushBuffers(); //gfxSwapBuffers(); - //Platform_LogConst("FRAME!"); - //if (gfx_vsync) gspWaitForVBlank(); if (gfx_minFrameMs) LimitFPS(); GPUBuffers_DeleteUnreferenced(); - //GPUTextures_DeleteUnreferenced(); - frameCounter++; + GPUTextures_DeleteUnreferenced(); + frameCounter1++; } void Gfx_OnWindowResize(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { } + /*########################################################################################################################* *----------------------------------------------------------Buffers--------------------------------------------------------* @@ -487,7 +619,7 @@ static void GPUBuffers_DeleteUnreferenced(void) { { next = buf->next; - if (buf->lastFrame + 4 > frameCounter) { + if (buf->lastFrame + 4 > frameCounter1) { // texture was used within last 4 fames prev = buf; } else { @@ -513,19 +645,21 @@ static void GPUBuffers_DeleteUnreferenced(void) { static cc_uint16* gfx_indices; GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { - struct GPUBuffer* buffer = GPUBuffer_Alloc(count, sizeof(cc_uint16)); - if (!buffer) Logger_Abort("Failed to allocate memory for index buffer"); + if (!gfx_indices) { + gfx_indices = linearAlloc(count * sizeof(cc_uint16)); + if (!gfx_indices) Logger_Abort("Failed to allocate memory for index buffer"); + } - fillFunc((cc_uint16*)buffer->data, count, obj); - return buffer; + fillFunc(gfx_indices, count, obj); + return gfx_indices; } void Gfx_BindIb(GfxResourceID ib) { - struct GPUBuffer* buffer = (struct GPUBuffer*)ib; - gfx_indices = (cc_uint16*)buffer->data; + u32 pa = osConvertVirtToPhys(ib); + GPUCMD_AddWrite(GPUREG_INDEXBUFFER_CONFIG, (pa - BUFFER_BASE_PADDR) | (C3D_UNSIGNED_SHORT << 31)); } -void Gfx_DeleteIb(GfxResourceID* ib) { GPUBuffer_Unref(ib); } +void Gfx_DeleteIb(GfxResourceID* ib) { } /*########################################################################################################################* @@ -539,19 +673,8 @@ static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { void Gfx_BindVb(GfxResourceID vb) { struct GPUBuffer* buffer = (struct GPUBuffer*)vb; - buffer->lastFrame = frameCounter; + buffer->lastFrame = frameCounter1; gfx_vertices = buffer->data; - - // https://github.com/devkitPro/citro3d/issues/47 - // "Fyi the permutation specifies the order in which the attributes are stored in the buffer, LSB first. So 0x210 indicates attributes 0, 1 & 2." - C3D_BufInfo* bufInfo = C3D_GetBufInfo(); - BufInfo_Init(bufInfo); - - if (gfx_format == VERTEX_FORMAT_TEXTURED) { - BufInfo_Add(bufInfo, buffer->data, SIZEOF_VERTEX_TEXTURED, 3, 0x210); - } else { - BufInfo_Add(bufInfo, buffer->data, SIZEOF_VERTEX_COLOURED, 2, 0x10); - } } void Gfx_DeleteVb(GfxResourceID* vb) { GPUBuffer_Unref(vb); } @@ -589,19 +712,81 @@ void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } /*########################################################################################################################* *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ +static u32 fogColor; +static C3D_FogLut fog_lut; +static int fogMode = FOG_LINEAR; +static float fogDensity = 1.0f; +static float fogEnd = 32.0f; + void Gfx_SetFog(cc_bool enabled) { + C3D_FogGasMode(enabled ? GPU_FOG : GPU_NO_FOG, GPU_PLAIN_DENSITY, false); + // TODO doesn't work quite right } void Gfx_SetFogCol(PackedCol color) { + // TODO find better way? + u32 c = (0xFFu << 24) | (PackedCol_B(color) << 16) | (PackedCol_G(color) << 8) | PackedCol_R(color); + if (c == fogColor) return; + + fogColor = c; + C3D_FogColor(c); +} + +static void ApplyFog(float* values) { + float data[256]; + + for (int i = 0; i <= 128; i ++) + { + float val = values[i]; + if (i < 128) data[i] = val; + if (i > 0) data[i + 127] = val - data[i-1]; + } + + FogLut_FromArray(&fog_lut, data); + C3D_FogLutBind(&fog_lut); +} + +static float GetFogValue(float c) { + if (fogMode == FOG_LINEAR) { + return (fogEnd - c) / fogEnd; + } else if (fogMode == FOG_EXP) { + return expf(-(fogDensity * c)); + } else { + return expf(-(fogDensity * c) * (fogDensity * c)); + } +} + +static void UpdateFog(void) { + float near = 0.01f; + float far = Game_ViewDistance; + float values[129]; + + // TODO: Exp calculation isn't right for lava ??? + for (int i = 0; i <= 128; i ++) + { + float c = FogLut_CalcZ(i / 128.0f, near, far); + values[i] = GetFogValue(c); + } + ApplyFog(values); } void Gfx_SetFogDensity(float value) { + if (fogDensity == value) return; + + fogDensity = value; + UpdateFog(); } void Gfx_SetFogEnd(float value) { + if (fogEnd == value) return; + + fogEnd = value; + UpdateFog(); } void Gfx_SetFogMode(FogFunc func) { + fogMode = func; + UpdateFog(); } @@ -612,7 +797,8 @@ static C3D_Mtx _view, _proj; void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { // See Mtx_OrthoTilt in Citro3D for the original basis - // (it's mostly just a standard orthograph matrix rotated by 90 degrees) + // (it's mostly just a standard orthograph matrix rotated by 90 degrees) + // Note: The rows/columns are "flipped" over the diagonal axis compared to original basis Mem_Set(matrix, 0, sizeof(struct Matrix)); matrix->row2.x = -2.0f / height; @@ -628,9 +814,11 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { // See Mtx_PerspTilt in Citro3D for the original basis // (it's mostly just a standard perspective matrix rotated by 90 degrees) + // Note: The rows/columns are "flipped" over the diagonal axis compared to original basis float zNear = 0.1f; fov = tanf(fov / 2.0f); Mem_Set(matrix, 0, sizeof(struct Matrix)); + matrix->row2.x = 1.0f / fov; matrix->row1.y = -1.0f / (fov * aspect); matrix->row4.z = zFar * zNear / (zNear - zFar); @@ -715,21 +903,23 @@ static void UpdateAttribFormat(VertexFormat fmt) { } static void UpdateTexEnv(VertexFormat fmt) { - C3D_TexEnv* env = C3D_GetTexEnv(0); - C3D_TexEnvInit(env); + int func, source; if (fmt == VERTEX_FORMAT_TEXTURED) { // Configure the first fragment shading substage to blend the texture color with // the vertex color (calculated by the vertex shader using a lighting algorithm) // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight - C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0); - C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); + source = GPU_TEVSOURCES(GPU_TEXTURE0, GPU_PRIMARY_COLOR, 0); + func = GPU_MODULATE; } else { // Configure the first fragment shading substage to just pass through the vertex color // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight - C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR, 0, 0); - C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); + source = GPU_TEVSOURCES(GPU_PRIMARY_COLOR, 0, 0); + func = GPU_REPLACE; } + + GPUCMD_AddWrite(GPUREG_TEXENV0_SOURCE, source | (source << 16)); + GPUCMD_AddWrite(GPUREG_TEXENV0_COMBINER, func | (func << 16)); } void Gfx_SetVertexFormat(VertexFormat fmt) { @@ -746,32 +936,48 @@ void Gfx_DrawVb_Lines(int verticesCount) { /* TODO */ } -static void SetVertexBuffer(int startVertex) { - C3D_BufInfo* bufInfo = C3D_GetBufInfo(); - BufInfo_Init(bufInfo); +static void SetVertexSource(int startVertex) { + // https://github.com/devkitPro/citro3d/issues/47 + // "Fyi the permutation specifies the order in which the attributes are stored in the buffer, LSB first. So 0x210 indicates attributes 0, 1 & 2." + const void* data; + int stride, attribs, permutation; if (gfx_format == VERTEX_FORMAT_TEXTURED) { - BufInfo_Add(bufInfo, (struct VertexTextured*)gfx_vertices + startVertex, SIZEOF_VERTEX_TEXTURED, 3, 0x210); + data = (struct VertexTextured*)gfx_vertices + startVertex; + stride = SIZEOF_VERTEX_TEXTURED; + attribs = 3; + permutation = 0x210; } else { - BufInfo_Add(bufInfo, (struct VertexColoured*)gfx_vertices + startVertex, SIZEOF_VERTEX_COLOURED, 2, 0x10); + data = (struct VertexColoured*)gfx_vertices + startVertex; + stride = SIZEOF_VERTEX_COLOURED; + attribs = 2; + permutation = 0x10; } + + u32 pa = osConvertVirtToPhys(data); + u32 args[3]; // GPUREG_ATTRIBBUFFER0_OFFSET, GPUREG_ATTRIBBUFFER0_CONFIG1, GPUREG_ATTRIBBUFFER0_CONFIG2 + + args[0] = pa - BUFFER_BASE_PADDR; + args[1] = permutation; + args[2] = (stride << 16) | (attribs << 28); + + GPUCMD_AddIncrementalWrites(GPUREG_ATTRIBBUFFER0_OFFSET, args, 3); + // NOTE: Can't use GPUREG_VERTEX_OFFSET, it only works when drawing non-indexed arrays } void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { - SetVertexBuffer(startVertex); - C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount), C3D_UNSIGNED_SHORT, gfx_indices); - // this doesn't work properly, because (index buffer + offset) must be aligned to 16 bytes - // C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount), C3D_UNSIGNED_SHORT, gfx_indices + startVertex); + SetVertexSource(startVertex); + C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount)); } void Gfx_DrawVb_IndexedTris(int verticesCount) { - SetVertexBuffer(0); - C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount), C3D_UNSIGNED_SHORT, gfx_indices); + SetVertexSource(0); + C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount)); } void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { - SetVertexBuffer(startVertex); - C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount), C3D_UNSIGNED_SHORT, gfx_indices); + SetVertexSource(startVertex); + C3D_DrawElements(GPU_TRIANGLES, ICOUNT(verticesCount)); } // TODO: TEMP HACK !! @@ -847,4 +1053,4 @@ void Gfx_Draw2DTexture(const struct Texture* tex, PackedCol color) { C3D_ImmSendAttrib(v[0].U, v[0].V, 0.0f, 0.0f); C3D_ImmDrawEnd(); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_D3D11.c b/src/Graphics_D3D11.c index 39858c3f2..19f57f626 100644 --- a/src/Graphics_D3D11.c +++ b/src/Graphics_D3D11.c @@ -201,7 +201,7 @@ static void D3D11_DoMipmaps(ID3D11Resource* texture, int x, int y, struct Bitmap if (prev != bmp->scan0) Mem_Free(prev); } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { ID3D11Texture2D* tex = NULL; ID3D11ShaderResourceView* view = NULL; HRESULT hr; @@ -218,7 +218,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo D3D11_SUBRESOURCE_DATA data; data.pSysMem = bmp->scan0; - data.SysMemPitch = bmp->width * 4; + data.SysMemPitch = rowWidth * 4; data.SysMemSlicePitch = 0; D3D11_SUBRESOURCE_DATA* src = &data; @@ -245,7 +245,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo hr = ID3D11Device_CreateShaderResourceView(device, tex, NULL, &view); if (hr) Logger_Abort2(hr, "Failed to create view"); - if (mipmaps) Gfx_UpdateTexturePart(view, 0, 0, bmp, mipmaps); + if (mipmaps) Gfx_UpdateTexture(view, 0, 0, bmp, rowWidth, mipmaps); return view; } @@ -270,10 +270,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i ID3D11Resource_Release(res); } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_DeleteTexture(GfxResourceID* texId) { ID3D11ShaderResourceView* view = (ID3D11ShaderResourceView*)(*texId); ID3D11Resource* res = NULL; @@ -290,8 +286,6 @@ void Gfx_DeleteTexture(GfxResourceID* texId) { *texId = NULL; } -void Gfx_SetTexturing(cc_bool enabled) { } - /*########################################################################################################################* *-------------------------------------------------------Index buffers-----------------------------------------------------* @@ -446,7 +440,7 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = zNear / (zNear - zFar); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { // Deliberately swap zNear/zFar in projection matrix calculation to produce // a projection matrix that results in a reversed depth buffer @@ -456,7 +450,7 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f // Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh // NOTE: This calculation is shared with Direct3D 9 backend - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); *matrix = Matrix_Identity; matrix->row1.x = c / aspect; @@ -652,12 +646,12 @@ static void RS_CreateRasterState(void) { ID3D11Device_CreateRasterizerState(device, &desc, &rs_states[1]); } -static void RS_UpdateViewport(void) { +void Gfx_SetViewport(int x, int y, int w, int h) { D3D11_VIEWPORT viewport; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - viewport.Width = Window_Main.Width; - viewport.Height = Window_Main.Height; + viewport.TopLeftX = x; + viewport.TopLeftY = y; + viewport.Width = w; + viewport.Height = h; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; ID3D11DeviceContext_RSSetViewports(context, 1, &viewport); @@ -676,7 +670,7 @@ static void RS_FreeRasterStates(void) { static void RS_Init(void) { RS_CreateRasterState(); - RS_UpdateViewport(); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); RS_UpdateRasterState(); } @@ -899,9 +893,13 @@ static float gfx_clearColor[4]; static cc_bool gfx_alphaBlending, gfx_colorEnabled = true; static cc_bool gfx_depthTest, gfx_depthWrite; -static void OM_Clear(void) { - ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer, gfx_clearColor); - ID3D11DeviceContext_ClearDepthStencilView(context, depthbufferView, D3D11_CLEAR_DEPTH, 0.0f, 0); +static void OM_Clear(GfxBuffers buffers) { + if (buffers & GFX_BUFFER_COLOR) { + ID3D11DeviceContext_ClearRenderTargetView(context, backbuffer, gfx_clearColor); + } + if (buffers & GFX_BUFFER_DEPTH) { + ID3D11DeviceContext_ClearDepthStencilView(context, depthbufferView, D3D11_CLEAR_DEPTH, 0.0f, 0); + } } static void OM_UpdateTarget(void) { @@ -1015,7 +1013,7 @@ static void OM_Free(void) { OM_FreeBlendStates(); } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { gfx_clearColor[0] = PackedCol_R(color) / 255.0f; gfx_clearColor[1] = PackedCol_G(color) / 255.0f; gfx_clearColor[2] = PackedCol_B(color) / 255.0f; @@ -1037,27 +1035,24 @@ void Gfx_SetAlphaBlending(cc_bool enabled) { OM_UpdateBlendState(); } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { gfx_colorEnabled = r; OM_UpdateBlendState(); + // TODO all channels } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ -static BitmapCol* D3D11_GetRow(struct Bitmap* bmp, int y) { - // You were expecting a BitmapCol*, but it was me, D3D11_MAPPED_SUBRESOURCE*! - // This is necessary because the stride of the mapped backbuffer often doesn't equal width of the bitmap - // e.g. with backbuffer width of 854, stride is 3456 bytes instead of expected 3416 (854*4) - // Therefore have to calculate row address manually instead of using Bitmap_GetRow - D3D11_MAPPED_SUBRESOURCE* buffer = (D3D11_MAPPED_SUBRESOURCE*)bmp->scan0; - +static BitmapCol* D3D11_GetRow(struct Bitmap* bmp, int y, void* ctx) { + D3D11_MAPPED_SUBRESOURCE* buffer = (D3D11_MAPPED_SUBRESOURCE*)ctx; char* row = (char*)buffer->pData + y * buffer->RowPitch; return (BitmapCol*)row; } @@ -1090,8 +1085,8 @@ cc_result Gfx_TakeScreenshot(struct Stream* output) { hr = ID3D11DeviceContext_Map(context, tmp, 0, D3D11_MAP_READ, 0, &buffer); if (hr) goto finished; { - Bitmap_Init(bmp, desc.Width, desc.Height, (BitmapCol*)&buffer); - hr = Png_Encode(&bmp, output, D3D11_GetRow, false); + Bitmap_Init(bmp, desc.Width, desc.Height, NULL); + hr = Png_Encode(&bmp, output, D3D11_GetRow, false, &buffer); } ID3D11DeviceContext_Unmap(context, tmp, 0); @@ -1106,7 +1101,10 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { gfx_vsync = vsync; } void Gfx_BeginFrame(void) { OM_UpdateTarget(); } -void Gfx_Clear(void) { OM_Clear(); } + +void Gfx_ClearBuffers(GfxBuffers buffers) { + OM_Clear(buffers); +} void Gfx_EndFrame(void) { // https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-present @@ -1175,7 +1173,7 @@ void Gfx_OnWindowResize(void) { if (hr) Logger_Abort2(hr, "Failed to resize swapchain"); OM_InitTargets(); - RS_UpdateViewport(); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); } static void InitPipeline(void) { diff --git a/src/Graphics_D3D9.c b/src/Graphics_D3D9.c index 9758b4972..8cf140ade 100644 --- a/src/Graphics_D3D9.c +++ b/src/Graphics_D3D9.c @@ -30,14 +30,13 @@ static float totalMem; static cc_bool fallbackRendering; static void D3D9_RestoreRenderStates(void); -static void D3D9_FreeResource(GfxResourceID* resource) { +static void D3D9_FreeResource(GfxResourceID resource) { cc_uintptr addr; ULONG refCount; IUnknown* unk; - unk = (IUnknown*)(*resource); + unk = (IUnknown*)resource; if (!unk) return; - *resource = 0; #ifdef __cplusplus refCount = unk->Release(); @@ -214,8 +213,8 @@ cc_bool Gfx_TryRestoreContext(void) { void Gfx_Free(void) { Gfx_FreeState(); - D3D9_FreeResource(&device); - D3D9_FreeResource(&d3d); + D3D9_FreeResource(device); device = NULL; + D3D9_FreeResource(d3d); d3d = NULL; } static void Gfx_FreeState(void) { @@ -248,13 +247,12 @@ static void Gfx_RestoreState(void) { /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ -static void D3D9_SetTextureData(IDirect3DTexture9* texture, struct Bitmap* bmp, int lvl) { +static void D3D9_SetTextureData(IDirect3DTexture9* texture, struct Bitmap* bmp, int rowWidth, int lvl) { D3DLOCKED_RECT rect; cc_result res = IDirect3DTexture9_LockRect(texture, lvl, &rect, NULL, 0); if (res) Logger_Abort2(res, "D3D9_LockTextureData"); - cc_uint32 size = Bitmap_DataSize(bmp->width, bmp->height); - Mem_Copy(rect.pBits, bmp->scan0, size); + CopyTextureData(rect.pBits, rect.Pitch, bmp, rowWidth << 2); res = IDirect3DTexture9_UnlockRect(texture, lvl); if (res) Logger_Abort2(res, "D3D9_UnlockTextureData"); @@ -264,7 +262,6 @@ static void D3D9_SetTexturePartData(IDirect3DTexture9* texture, int x, int y, co D3DLOCKED_RECT rect; cc_result res; RECT part; - part.left = x; part.right = x + bmp->width; part.top = y; part.bottom = y + bmp->height; @@ -272,6 +269,7 @@ static void D3D9_SetTexturePartData(IDirect3DTexture9* texture, int x, int y, co if (res) Logger_Abort2(res, "D3D9_LockTexturePartData"); CopyTextureData(rect.pBits, rect.Pitch, bmp, rowWidth << 2); + res = IDirect3DTexture9_UnlockRect(texture, lvl); if (res) Logger_Abort2(res, "D3D9_UnlockTexturePartData"); } @@ -296,7 +294,7 @@ static void D3D9_DoMipmaps(IDirect3DTexture9* texture, int x, int y, struct Bitm if (partial) { D3D9_SetTexturePartData(texture, x, y, &mipmap, width, lvl); } else { - D3D9_SetTextureData(texture, &mipmap, lvl); + D3D9_SetTextureData(texture, &mipmap, width, lvl); } if (prev != bmp->scan0) Mem_Free(prev); @@ -329,14 +327,14 @@ static IDirect3DTexture9* DoCreateTexture(struct Bitmap* bmp, int levels, int po return tex; } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { IDirect3DTexture9* tex; IDirect3DTexture9* sys; cc_result res; int mipmapsLevels = CalcMipmapsLevels(bmp->width, bmp->height); int levels = 1 + (mipmaps ? mipmapsLevels : 0); - + if (flags & TEXTURE_FLAG_MANAGED) { while ((res = IDirect3DDevice9_CreateTexture(device, bmp->width, bmp->height, levels, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, NULL))) @@ -351,20 +349,20 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo } } - D3D9_SetTextureData(tex, bmp, 0); - if (mipmaps) D3D9_DoMipmaps(tex, 0, 0, bmp, bmp->width, false); + D3D9_SetTextureData(tex, bmp, rowWidth, 0); + if (mipmaps) D3D9_DoMipmaps(tex, 0, 0, bmp, rowWidth, false); return tex; } sys = DoCreateTexture(bmp, levels, D3DPOOL_SYSTEMMEM); - D3D9_SetTextureData(sys, bmp, 0); - if (mipmaps) D3D9_DoMipmaps(sys, 0, 0, bmp, bmp->width, false); + D3D9_SetTextureData(sys, bmp, rowWidth, 0); + if (mipmaps) D3D9_DoMipmaps(sys, 0, 0, bmp, rowWidth, false); tex = DoCreateTexture(bmp, levels, D3DPOOL_DEFAULT); res = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9*)sys, (IDirect3DBaseTexture9*)tex); if (res) Logger_Abort2(res, "D3D9_CreateTexture - Update"); - D3D9_FreeResource(&sys); + D3D9_FreeResource(sys); return tex; } @@ -374,18 +372,12 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i if (mipmaps) D3D9_DoMipmaps(texture, x, y, part, rowWidth, true); } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_BindTexture(GfxResourceID texId) { cc_result res = IDirect3DDevice9_SetTexture(device, 0, (IDirect3DBaseTexture9*)texId); if (res) Logger_Abort2(res, "D3D9_BindTexture"); } -void Gfx_DeleteTexture(GfxResourceID* texId) { D3D9_FreeResource(texId); } - -void Gfx_SetTexturing(cc_bool enabled) { } +void Gfx_DeleteTexture(GfxResourceID* texId) { D3D9_FreeResource(*texId); *texId = NULL; } void Gfx_EnableMipmaps(void) { if (!Gfx.Mipmaps) return; @@ -485,8 +477,9 @@ void Gfx_SetAlphaArgBlend(cc_bool enabled) { IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAOP, op); } -void Gfx_ClearCol(PackedCol color) { gfx_clearColor = color; } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +void Gfx_ClearColor(PackedCol color) { gfx_clearColor = color; } + +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { DWORD channels = (r ? 1u : 0u) | (g ? 2u : 0u) | (b ? 4u : 0u) | (a ? 8u : 0u); if (Gfx.LostContext) return; IDirect3DDevice9_SetRenderState(device, D3DRS_COLORWRITEENABLE, channels); @@ -506,7 +499,8 @@ void Gfx_SetDepthWrite(cc_bool enabled) { void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); if (depthOnly) IDirect3DDevice9_SetTexture(device, 0, NULL); /* For when Direct3D9 device doesn't support D3DRS_COLORWRITEENABLE */ @@ -580,7 +574,7 @@ void Gfx_BindIb(GfxResourceID ib) { if (res) Logger_Abort2(res, "D3D9_BindIb"); } -void Gfx_DeleteIb(GfxResourceID* ib) { D3D9_FreeResource(ib); } +void Gfx_DeleteIb(GfxResourceID* ib) { D3D9_FreeResource(*ib); *ib = NULL; } /*########################################################################################################################* @@ -625,7 +619,7 @@ static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { return D3D9_AllocVertexBuffer(fmt, count, D3DUSAGE_WRITEONLY); } -void Gfx_DeleteVb(GfxResourceID* vb) { D3D9_FreeResource(vb); } +void Gfx_DeleteVb(GfxResourceID* vb) { D3D9_FreeResource(*vb); *vb = NULL; } void Gfx_BindVb(GfxResourceID vb) { IDirect3DVertexBuffer9* vbuffer = (IDirect3DVertexBuffer9*)vb; @@ -651,7 +645,7 @@ static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { return D3D9_AllocVertexBuffer(fmt, maxVertices, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY); } -void Gfx_DeleteDynamicVb(GfxResourceID* vb) { D3D9_FreeResource(vb); } +void Gfx_DeleteDynamicVb(GfxResourceID* vb) { D3D9_FreeResource(*vb); *vb = NULL; } void Gfx_BindDynamicVb(GfxResourceID vb) { IDirect3DVertexBuffer9* vbuffer = (IDirect3DVertexBuffer9*)vb; @@ -766,7 +760,7 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = zNear / (zNear - zFar); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { /* Deliberately swap zNear/zFar in projection matrix calculation to produce */ /* a projection matrix that results in a reversed depth buffer */ @@ -776,7 +770,7 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */ /* NOTE: This calculation is shared with Direct3D 11 backend */ - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); *matrix = Matrix_Identity; matrix->row1.x = c / aspect; @@ -813,15 +807,15 @@ cc_result Gfx_TakeScreenshot(struct Stream* output) { if (res) goto finished; { Bitmap_Init(bmp, desc.Width, desc.Height, (BitmapCol*)rect.pBits); - res = Png_Encode(&bmp, output, NULL, false); + res = Png_Encode(&bmp, output, NULL, false, NULL); if (res) { IDirect3DSurface9_UnlockRect(temp); goto finished; } } res = IDirect3DSurface9_UnlockRect(temp); if (res) goto finished; finished: - D3D9_FreeResource(&backbuffer); - D3D9_FreeResource(&temp); + D3D9_FreeResource(backbuffer); + D3D9_FreeResource(temp); return res; } @@ -842,9 +836,12 @@ void Gfx_BeginFrame(void) { IDirect3DDevice9_BeginScene(device); } -void Gfx_Clear(void) { - DWORD flags = D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER; - cc_result res = IDirect3DDevice9_Clear(device, 0, NULL, flags, gfx_clearColor, 0.0f, 0); +void Gfx_ClearBuffers(GfxBuffers buffers) { + DWORD targets = 0; + if (buffers & GFX_BUFFER_COLOR) targets |= D3DCLEAR_TARGET; + if (buffers & GFX_BUFFER_DEPTH) targets |= D3DCLEAR_ZBUFFER; + + cc_result res = IDirect3DDevice9_Clear(device, 0, NULL, targets, gfx_clearColor, 0.0f, 0); if (res) Logger_Abort2(res, "D3D9_Clear"); } @@ -889,4 +886,6 @@ void Gfx_OnWindowResize(void) { /* Only resize when necessary */ UpdateSwapchain(" (resizing window)"); } + +void Gfx_SetViewport(int x, int y, int w, int h) { } #endif diff --git a/src/Graphics_Dreamcast.c b/src/Graphics_Dreamcast.c index 7237f4af1..29f9e3145 100644 --- a/src/Graphics_Dreamcast.c +++ b/src/Graphics_Dreamcast.c @@ -4,7 +4,7 @@ #include "Errors.h" #include "Logger.h" #include "Window.h" -#include "../third_party/gldc/include/gldc.h" +#include "../third_party/gldc/gldc.h" #include #include #include @@ -15,9 +15,26 @@ static cc_bool renderingDisabled; /*########################################################################################################################* *---------------------------------------------------------General---------------------------------------------------------* *#########################################################################################################################*/ +static void InitGLState(void) { + glClearDepth(1.0f); + glDepthMask(GL_TRUE); + glShadeModel(GL_SMOOTH); + + glDisable(GL_ALPHA_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDisable(GL_FOG); +} + void Gfx_Create(void) { if (!Gfx.Created) glKosInit(); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); + InitGLState(); + Gfx.MinTexWidth = 8; + Gfx.MinTexHeight = 8; Gfx.MaxTexWidth = 1024; Gfx.MaxTexHeight = 1024; Gfx.MaxTexSize = 512 * 512; // reasonable cap as Dreamcast only has 8MB VRAM @@ -45,7 +62,7 @@ void Gfx_SetFaceCulling(cc_bool enabled) { gl_Toggle(GL_CULL_FACE); } void Gfx_SetAlphaBlending(cc_bool enabled) { gl_Toggle(GL_BLEND); } void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { if (color == gfx_clearColor) return; gfx_clearColor = color; @@ -55,15 +72,13 @@ void Gfx_ClearCol(PackedCol color) { pvr_set_bg_color(r, g, b); // TODO: not working ? } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { // TODO: Doesn't work } void Gfx_SetDepthWrite(cc_bool enabled) { glDepthMask(enabled); } void Gfx_SetDepthTest(cc_bool enabled) { gl_Toggle(GL_DEPTH_TEST); } -void Gfx_SetTexturing(cc_bool enabled) { } - void Gfx_SetAlphaTest(cc_bool enabled) { gl_Toggle(GL_ALPHA_TEST); } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { @@ -89,10 +104,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */ /* For a FOV based perspective matrix, left/right/top/bottom are calculated as: */ @@ -254,7 +269,7 @@ static unsigned Interleave(unsigned x) { #define BGRA8_to_BGRA4(src) \ ((src[0] & 0xF0) >> 4) | (src[1] & 0xF0) | ((src[2] & 0xF0) << 4) | ((src[3] & 0xF0) << 8); -static void ConvertTexture(cc_uint16* dst, struct Bitmap* bmp) { +static void ConvertTexture(cc_uint16* dst, struct Bitmap* bmp, int rowWidth) { unsigned min_dimension; unsigned interleave_mask, interleaved_bits; unsigned shifted_mask, shift_bits; @@ -262,10 +277,11 @@ static void ConvertTexture(cc_uint16* dst, struct Bitmap* bmp) { unsigned lo_X, hi_X, X; Twiddle_CalcFactors(bmp->width, bmp->height); - cc_uint8* src = (cc_uint8*)bmp->scan0; for (int y = 0; y < bmp->height; y++) { Twiddle_CalcY(y); + cc_uint8* src = (cc_uint8*)(bmp->scan0 + y * rowWidth); + for (int x = 0; x < bmp->width; x++, src += 4) { Twiddle_CalcX(x); @@ -274,7 +290,7 @@ static void ConvertTexture(cc_uint16* dst, struct Bitmap* bmp) { } } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { GLuint texId = gldcGenTexture(); gldcBindTexture(texId); @@ -284,7 +300,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo void* pixels; GLsizei width, height; gldcGetTexture(&pixels, &width, &height); - ConvertTexture(pixels, bmp); + ConvertTexture(pixels, bmp, rowWidth); return texId; } @@ -326,10 +342,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i // TODO: Do we need to flush VRAM? } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - /*########################################################################################################################* *-----------------------------------------------------State management----------------------------------------------------* @@ -531,7 +543,9 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { } void Gfx_BeginFrame(void) { } -void Gfx_Clear(void) { + +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO clear only some buffers // no need to use glClear } @@ -541,6 +555,17 @@ void Gfx_EndFrame(void) { } void Gfx_OnWindowResize(void) { - glViewport(0, 0, Game.Width, Game.Height); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); } -#endif \ No newline at end of file + +void Gfx_SetViewport(int x, int y, int w, int h) { + if (x == 0 && y == 0 && w == Game.Width && h == Game.Height) { + glDisable(GL_SCISSOR_TEST); + } else { + glEnable(GL_SCISSOR_TEST); + } + + glViewport(x, y, w, h); + glScissor (x, y, w, h); +} +#endif diff --git a/src/Graphics_GCWii.c b/src/Graphics_GCWii.c index 0f9f3e97a..923b0cff3 100644 --- a/src/Graphics_GCWii.c +++ b/src/Graphics_GCWii.c @@ -27,9 +27,8 @@ static void InitGX(void) { memset(fifo_buffer, 0, FIFO_SIZE); GX_Init(fifo_buffer, FIFO_SIZE); - GX_SetViewport(0, 0, mode->fbWidth, mode->efbHeight, 0, 1); + Gfx_SetViewport(0, 0, mode->fbWidth, mode->efbHeight); GX_SetDispCopyYScale((f32)mode->xfbHeight / (f32)mode->efbHeight); - GX_SetScissor(0, 0, mode->fbWidth, mode->efbHeight); GX_SetDispCopySrc(0, 0, mode->fbWidth, mode->efbHeight); GX_SetDispCopyDst(mode->fbWidth, mode->xfbHeight); GX_SetCopyFilter(mode->aa, mode->sample_pattern, @@ -50,8 +49,12 @@ static void InitGX(void) { void Gfx_Create(void) { if (!Gfx.Created) InitGX(); - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; + + Gfx.MinTexWidth = 4; + Gfx.MinTexHeight = 4; Gfx.Created = true; gfx_vsync = true; @@ -136,20 +139,16 @@ static void ReorderPixels(CCTexture* tex, struct Bitmap* bmp, } } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { - if (bmp->width < 4 || bmp->height < 4) { - Platform_LogConst("ERROR: Tried to create texture smaller than 4x4"); - return 0; - } - +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { int size = bmp->width * bmp->height * 4; CCTexture* tex = (CCTexture*)memalign(32, 32 + size); + if (!tex) return NULL; GX_InitTexObj(&tex->obj, tex->pixels, bmp->width, bmp->height, GX_TF_RGBA8, GX_REPEAT, GX_REPEAT, GX_FALSE); GX_InitTexObjFilterMode(&tex->obj, GX_NEAR, GX_NEAR); - ReorderPixels(tex, bmp, 0, 0, bmp->width); + ReorderPixels(tex, bmp, 0, 0, rowWidth); DCFlushRange(tex->pixels, size); return tex; } @@ -161,10 +160,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i GX_InvalidateTexAll(); } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_DeleteTexture(GfxResourceID* texId) { GfxResourceID data = *texId; if (data) Mem_Free(data); @@ -203,7 +198,7 @@ void Gfx_SetAlphaBlending(cc_bool enabled) { void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { gfx_clearColor.r = PackedCol_R(color); gfx_clearColor.g = PackedCol_G(color); gfx_clearColor.b = PackedCol_B(color); @@ -211,7 +206,8 @@ void Gfx_ClearCol(PackedCol color) { GX_SetCopyClear(gfx_clearColor, 0x00ffffff); // TODO: use GX_MAX_Z24 } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO } static cc_bool depth_write = true, depth_test = true; @@ -235,8 +231,53 @@ void Gfx_SetDepthTest(cc_bool enabled) { /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ +static BitmapCol* GCWii_GetRow(struct Bitmap* bmp, int y, void* ctx) { + u8* buffer = (u8*)ctx; + u8 a, r, g, b; + int blockYStride = 4 * (bmp->width * 4); // tile row stride = 4 * row stride + int blockXStride = (4 * 4) * 4; // 16 pixels per tile + + // Do the inverse of converting from 4x4 tiled to linear + for (u32 x = 0; x < bmp->width; x++){ + int tileY = y >> 2, tileX = x >> 2; + int locY = y & 0x3, locX = x & 0x3; + int idx = (tileY * blockYStride) + (tileX * blockXStride) + ((locY << 2) + locX) * 2; + + // All 16 pixels are stored with AR first, then GB + //a = buffer[idx ]; + r = buffer[idx + 1]; + g = buffer[idx + 32]; + b = buffer[idx + 33]; + + bmp->scan0[x] = BitmapColor_RGB(r, g, b); + } + return bmp->scan0; +} + cc_result Gfx_TakeScreenshot(struct Stream* output) { - return ERR_NOT_SUPPORTED; + BitmapCol tmp[1024]; + GXRModeObj* vmode = VIDEO_GetPreferredMode(NULL); + int width = vmode->fbWidth; + int height = vmode->efbHeight; + + u8* buffer = memalign(32, width * height * 4); + if (!buffer) return ERR_OUT_OF_MEMORY; + + GX_SetTexCopySrc(0, 0, width, height); + GX_SetTexCopyDst(width, height, GX_TF_RGBA8, GX_FALSE); + GX_CopyTex(buffer, GX_FALSE); + GX_PixModeSync(); + GX_Flush(); + DCFlushRange(buffer, width * height * 4); + + struct Bitmap bmp; + bmp.scan0 = tmp; + bmp.width = width; + bmp.height = height; + + cc_result res = Png_Encode(&bmp, output, GCWii_GetRow, false, buffer); + free(buffer); + return res; } void Gfx_GetApiInfo(cc_string* info) { @@ -252,7 +293,8 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { void Gfx_BeginFrame(void) { } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO clear only some buffers } void Gfx_EndFrame(void) { @@ -269,6 +311,11 @@ void Gfx_EndFrame(void) { void Gfx_OnWindowResize(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { + GX_SetViewport(x, y, w, h, 0, 1); + GX_SetScissor(x, y, w, h); +} + cc_bool Gfx_WarnIfNecessary(void) { return false; } @@ -280,7 +327,7 @@ cc_bool Gfx_WarnIfNecessary(void) { return false; } GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { //fillFunc(gfx_indices, count, obj); // not used since render using GX_QUADS anyways - return 1; + return (void*)1; } void Gfx_BindIb(GfxResourceID ib) { } @@ -399,10 +446,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -zFar / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); // Transposed, source guPersepctive https://github.com/devkitPro/libogc/blob/master/libogc/gu.c *matrix = Matrix_Identity; @@ -550,4 +597,4 @@ void Gfx_DrawVb_IndexedTris(int verticesCount) { void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { Draw_TexturedTriangles(verticesCount, startVertex); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_GL1.c b/src/Graphics_GL1.c index cbaca73ba..ea26aa58a 100644 --- a/src/Graphics_GL1.c +++ b/src/Graphics_GL1.c @@ -534,15 +534,14 @@ void Gfx_SetFogMode(FogFunc func) { gfx_fogMode = func; } -void Gfx_SetTexturing(cc_bool enabled) { } - void Gfx_SetAlphaTest(cc_bool enabled) { if (enabled) { glEnable(GL_ALPHA_TEST); } else { glDisable(GL_ALPHA_TEST); } } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); if (enabled) { glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } } @@ -590,20 +589,27 @@ static void Gfx_RestoreState(void) { cc_bool Gfx_WarnIfNecessary(void) { cc_string renderer = String_FromReadonly((const char*)glGetString(GL_RENDERER)); - + #ifdef CC_BUILD_GL11 Chat_AddRaw("&cYou are using the very outdated OpenGL backend."); Chat_AddRaw("&cAs such you may experience poor performance."); Chat_AddRaw("&cIt is likely you need to install video card drivers."); #endif - if (!String_ContainsConst(&renderer, "Intel")) return false; - Chat_AddRaw("&cIntel graphics cards are known to have issues with the OpenGL build."); - Chat_AddRaw("&cVSync may not work, and you may see disappearing clouds and map edges."); -#ifdef CC_BUILD_WIN - Chat_AddRaw("&cTry downloading the Direct3D 9 build instead."); -#endif - return true; + if (String_ContainsConst(&renderer, "llvmpipe")) { + Chat_AddRaw("&cSoftware rendering is being used, performance will greatly suffer."); + Chat_AddRaw("&cVSync may not work, and you may see disappearing clouds and map edges."); + return true; + } + if (String_ContainsConst(&renderer, "Intel")) { + Chat_AddRaw("&cIntel graphics cards are known to have issues with the OpenGL build."); + Chat_AddRaw("&cVSync may not work, and you may see disappearing clouds and map edges."); + #ifdef CC_BUILD_WIN + Chat_AddRaw("&cTry downloading the Direct3D 9 build instead."); + #endif + return true; + } + return false; } diff --git a/src/Graphics_GL2.c b/src/Graphics_GL2.c index cc036598d..e1aa6c6dc 100644 --- a/src/Graphics_GL2.c +++ b/src/Graphics_GL2.c @@ -500,12 +500,12 @@ void Gfx_SetFogMode(FogFunc func) { SwitchProgram(); } -void Gfx_SetTexturing(cc_bool enabled) { } void Gfx_SetAlphaTest(cc_bool enabled) { gfx_alphaTest = enabled; SwitchProgram(); } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } @@ -546,7 +546,17 @@ static void GLBackend_Init(void) { #endif #ifdef CC_BUILD_GLES - // OpenGL ES 2.0 doesn't support custom mipmaps levels + // OpenGL ES 2.0 doesn't support custom mipmaps levels, but 3.2 does + // Note that GL_MAJOR_VERSION and GL_MINOR_VERSION were not actually + // implemented until 3.0.. but hopefully older GPU drivers out there + // don't try and set a value even when it's unsupported + #define _GL_MAJOR_VERSION 33307 + #define _GL_MINOR_VERSION 33308 + + GLint major = 0, minor = 0; + glGetIntegerv(_GL_MAJOR_VERSION, &major); + glGetIntegerv(_GL_MINOR_VERSION, &minor); + customMipmapsLevels = major >= 3 && minor >= 2; #else customMipmapsLevels = true; const GLubyte* ver = glGetString(GL_VERSION); diff --git a/src/Graphics_N64.c b/src/Graphics_N64.c index d08480978..1b26f0458 100644 --- a/src/Graphics_N64.c +++ b/src/Graphics_N64.c @@ -79,6 +79,8 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { void Gfx_OnWindowResize(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { } + void Gfx_BeginFrame(void) { surface_t* disp = display_get(); @@ -90,13 +92,17 @@ void Gfx_BeginFrame(void) { extern void __rdpq_autosync_change(int mode); static color_t gfx_clearColor; -void Gfx_Clear(void) { + +void Gfx_ClearBuffers(GfxBuffers buffers) { __rdpq_autosync_change(AUTOSYNC_PIPE); - rdpq_clear(gfx_clearColor); - rdpq_clear_z(0xFFFC); + + if (buffers & GFX_BUFFER_COLOR) + rdpq_clear(gfx_clearColor); + if (buffers & GFX_BUFFER_DEPTH) + rdpq_clear_z(0xFFFC); } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { gfx_clearColor.r = PackedCol_R(color); gfx_clearColor.g = PackedCol_G(color); gfx_clearColor.b = PackedCol_B(color); @@ -128,7 +134,7 @@ typedef struct CCTexture { #define To16BitPixel(src) \ ((src & 0x80) >> 7) | ((src & 0xF800) >> 10) | ((src & 0xF80000) >> 13) | ((src & 0xF8000000) >> 16); -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { cc_bool bit16 = flags & TEXTURE_FLAG_LOWRES; // rows are actually 8 byte aligned in TMEM https://github.com/DragonMinded/libdragon/blob/f360fa1bb1fb3ff3d98f4ab58692d40c828636c9/src/rdpq/rdpq_tex.c#L132 // so even though width * height * pixel size may fit within 4096 bytes, after adjusting for 8 byte alignment, row pitch * height may exceed 4096 bytes @@ -143,16 +149,17 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipmaps ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mipmaps ? GL_LINEAR : GL_NEAREST); - tex->surface = surface_alloc(bit16 ? FMT_RGBA16 : FMT_RGBA32, bmp->width, bmp->height); - surface_t* fb = &tex->surface; - cc_uint32* src = (cc_uint32*)bmp->scan0; - cc_uint8* dst = (cc_uint8*)fb->buffer; + tex->surface = surface_alloc(bit16 ? FMT_RGBA16 : FMT_RGBA32, bmp->width, bmp->height); + surface_t* fb = &tex->surface; if (bit16) { + cc_uint32* src = (cc_uint32*)bmp->scan0; + cc_uint8* dst = (cc_uint8*)fb->buffer; + // 16 bpp requires reducing A8R8G8B8 to A1R5G5B5 for (int y = 0; y < bmp->height; y++) { - cc_uint32* src_row = src + y * bmp->width; + cc_uint32* src_row = src + y * rowWidth; cc_uint16* dst_row = (cc_uint16*)(dst + y * fb->stride); for (int x = 0; x < bmp->width; x++) @@ -162,12 +169,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo } } else { // 32 bpp can just be copied straight across - for (int y = 0; y < bmp->height; y++) - { - Mem_Copy(dst + y * fb->stride, - src + y * bmp->width, - bmp->width * 4); - } + CopyTextureData(fb->buffer, fb->stride, bmp, rowWidth << 2); } @@ -218,10 +220,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i glSurfaceTexImageN64(GL_TEXTURE_2D, 0, fb, ¶ms); } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_DeleteTexture(GfxResourceID* texId) { CCTexture* tex = (CCTexture*)(*texId); if (!tex) return; @@ -242,7 +240,7 @@ void Gfx_SetFaceCulling(cc_bool enabled) { gl_Toggle(GL_CULL_FACE); } void Gfx_SetAlphaBlending(cc_bool enabled) { gl_Toggle(GL_BLEND); } void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { //glColorMask(r, g, b, a); TODO } @@ -283,10 +281,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */ /* For a FOV based perspective matrix, left/right/top/bottom are calculated as: */ @@ -379,8 +377,6 @@ void Gfx_SetFogEnd(float value) { void Gfx_SetFogMode(FogFunc func) { } -void Gfx_SetTexturing(cc_bool enabled) { } - void Gfx_SetAlphaTest(cc_bool enabled) { if (enabled) { glEnable(GL_ALPHA_TEST); } else { glDisable(GL_ALPHA_TEST); } } @@ -388,7 +384,8 @@ void Gfx_SetAlphaTest(cc_bool enabled) { void Gfx_DepthOnlyRendering(cc_bool depthOnly) { depthOnlyRendering = depthOnly; // TODO: Better approach? maybe using glBlendFunc instead? cc_bool enabled = !depthOnly; - //Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + //SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + // enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); if (enabled) { glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } } diff --git a/src/Graphics_NDS.c b/src/Graphics_NDS.c index b6d3f8353..ba081eaa5 100644 --- a/src/Graphics_NDS.c +++ b/src/Graphics_NDS.c @@ -11,19 +11,26 @@ *#########################################################################################################################*/ void Gfx_Create(void) { Gfx_RestoreState(); - - videoSetMode(MODE_0_3D); + + Gfx.MinTexWidth = 8; + Gfx.MinTexHeight = 8; + Gfx.MaxTexWidth = 256; + Gfx.MaxTexHeight = 256; + //Gfx.MaxTexSize = 256 * 256; + Gfx.Created = true; glInit(); glClearColor(0, 15, 10, 31); glClearPolyID(63); + glAlphaFunc(7); - glClearDepth(0x7FFF); - + glClearDepth(GL_MAX_DEPTH); glViewport(0, 0, 255, 191); vramSetBankA(VRAM_A_TEXTURE); - // setup memory for textures + vramSetBankB(VRAM_B_TEXTURE); + vramSetBankC(VRAM_C_TEXTURE); + vramSetBankD(VRAM_D_TEXTURE); glPolyFmt(POLY_ALPHA(31) | POLY_CULL_NONE); } @@ -34,6 +41,10 @@ cc_bool Gfx_TryRestoreContext(void) { void Gfx_Free(void) { Gfx_FreeState(); + vramSetBankA(VRAM_A_LCD); + vramSetBankB(VRAM_B_LCD); + vramSetBankC(VRAM_C_LCD); + vramSetBankD(VRAM_D_LCD); } @@ -57,14 +68,16 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { void Gfx_OnWindowResize(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { } + void Gfx_BeginFrame(void) { - Platform_LogConst("FRAME"); } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { int R = PackedCol_R(color) >> 3; int G = PackedCol_G(color) >> 3; int B = PackedCol_B(color) >> 3; @@ -83,21 +96,73 @@ void Gfx_EndFrame(void) { /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { - return NULL; +// B8 G8 R8 A8 > R5 G5 B5 A1 +#define BGRA8_to_DS(src) \ + ((src[2] & 0xF8) >> 3) | ((src[1] & 0xF8) << 2) | ((src[0] & 0xF8) << 7) | ((src[3] & 0x80) << 8); + +static void ConvertTexture(cc_uint16* dst, struct Bitmap* bmp, int rowWidth) { + for (int y = 0; y < bmp->height; y++) + { + cc_uint8* src = (cc_uint8*)(bmp->scan0 + y * rowWidth); + + for (int x = 0; x < bmp->width; x++, src += 4) + { + *dst++ = BGRA8_to_DS(src); + } + } +} + +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + vramSetBankA(VRAM_A_TEXTURE); + + cc_uint16* tmp = Mem_TryAlloc(bmp->width * bmp->height, 2); + if (!tmp) return 0; + ConvertTexture(tmp, bmp, rowWidth); + + int textureID; + glGenTextures(1, &textureID); + glBindTexture(0, textureID); + glTexImage2D(0, 0, GL_RGBA, bmp->width, bmp->height, 0, TEXGEN_TEXCOORD, tmp); + glTexParameter(0, GL_TEXTURE_WRAP_S | GL_TEXTURE_WRAP_T); + + cc_uint16* vram_ptr = glGetTexturePointer(textureID); + if (!vram_ptr) Platform_Log2("No VRAM for %i x %i texture", &bmp->width, &bmp->height); + + Mem_Free(tmp); + return (void*)textureID; } void Gfx_BindTexture(GfxResourceID texId) { - + glBindTexture(0, (int)texId); } void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { -} + int texture = (int)texId; + glBindTexture(0, texture); + + int width = 0; + glGetInt(GL_GET_TEXTURE_WIDTH, &width); + cc_uint16* vram_ptr = glGetTexturePointer(texture); + return; + // TODO doesn't work without VRAM bank changing to LCD and back maybe?? + // (see what glTeximage2D does ??) -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { + for (int yy = 0; yy < part->height; yy++) + { + cc_uint16* dst = vram_ptr + width * (y + yy) + x; + cc_uint8* src = (cc_uint8*)(part->scan0 + rowWidth * yy); + + for (int xx = 0; xx < part->width; xx++, src += 4, dst++) + { + *dst = BGRA8_to_DS(src); + } + } } void Gfx_DeleteTexture(GfxResourceID* texId) { + int texture = (int)(*texId); + if (texture) glDeleteTextures(1, &texture); + *texId = 0; } void Gfx_EnableMipmaps(void) { } @@ -107,12 +172,22 @@ void Gfx_DisableMipmaps(void) { } /*########################################################################################################################* *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ -void Gfx_SetFaceCulling(cc_bool enabled) { } -void Gfx_SetAlphaBlending(cc_bool enabled) { } +void Gfx_SetFaceCulling(cc_bool enabled) { + glPolyFmt(POLY_ALPHA(31) | (enabled ? POLY_CULL_BACK : POLY_CULL_NONE)); +} + +void Gfx_SetAlphaBlending(cc_bool enabled) { + /*if (enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + }*/ +} + void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { - +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO } void Gfx_SetDepthWrite(cc_bool enabled) { } @@ -123,7 +198,7 @@ static void Gfx_RestoreState(void) { InitDefaultResources(); } -cc_bool Gfx_WarnIfNecessary(void) { return false; } +cc_bool Gfx_WarnIfNecessary(void) { return true; } /*########################################################################################################################* @@ -133,6 +208,8 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glortho */ /* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */ *matrix = Matrix_Identity; + width /= 64.0f; + height /= 64.0f; matrix->row1.x = 2.0f / width; matrix->row2.y = -2.0f / height; @@ -143,10 +220,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */ /* For a FOV based perspective matrix, left/right/top/bottom are calculated as: */ @@ -166,8 +243,67 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f /*########################################################################################################################* *----------------------------------------------------------Buffers--------------------------------------------------------* *#########################################################################################################################*/ +// Preprocess vertex buffers into optimised layout for DS +static VertexFormat buf_fmt; +static int buf_count; + static void* gfx_vertices; +struct DSTexturedVertex { + vu32 xy; v16 z; + vu32 rgb; + int u, v; +}; +struct DSColouredVertex { + vu32 xy; v16 z; + vu32 rgb; +}; + +// Precalculate all the expensive vertex data conversion, +// so that actual drawing of them is as fast as possible +static void PreprocessTexturedVertices(void) { + struct VertexTextured* src = gfx_vertices; + struct DSTexturedVertex* dst = gfx_vertices; + + for (int i = 0; i < buf_count; i++, src++, dst++) + { + struct VertexTextured v = *src; + v16 x = floattov16(v.x / 64.0f); + v16 y = floattov16(v.y / 64.0f); + v16 z = floattov16(v.z / 64.0f); + dst->xy = (y << 16) | (x & 0xFFFF); + dst->z = z; + + dst->u = floattof32(v.U); + dst->v = floattof32(v.V); + + int r = PackedCol_R(v.Col); + int g = PackedCol_G(v.Col); + int b = PackedCol_B(v.Col); + dst->rgb = RGB15(r >> 3, g >> 3, b >> 3); + } +} + +static void PreprocessColouredVertices(void) { + struct VertexColoured* src = gfx_vertices; + struct DSColouredVertex* dst = gfx_vertices; + + for (int i = 0; i < buf_count; i++, src++, dst++) + { + struct VertexColoured v = *src; + v16 x = floattov16(v.x / 64.0f); + v16 y = floattov16(v.y / 64.0f); + v16 z = floattov16(v.z / 64.0f); + dst->xy = (y << 16) | (x & 0xFFFF); + dst->z = z; + + int r = PackedCol_R(v.Col); + int g = PackedCol_G(v.Col); + int b = PackedCol_B(v.Col); + dst->rgb = RGB15(r >> 3, g >> 3, b >> 3); + } +} + GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { return (void*)1; } @@ -189,10 +325,20 @@ void Gfx_DeleteVb(GfxResourceID* vb) { } void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { + buf_fmt = fmt; + buf_count = count; return vb; } -void Gfx_UnlockVb(GfxResourceID vb) { gfx_vertices = vb; } +void Gfx_UnlockVb(GfxResourceID vb) { + gfx_vertices = vb; + + if (buf_fmt == VERTEX_FORMAT_TEXTURED) { + PreprocessTexturedVertices(); + } else { + PreprocessColouredVertices(); + } +} static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { @@ -202,7 +348,7 @@ static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); } void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { - return vb; + return Gfx_LockVb(vb, fmt, count); } void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_UnlockVb(vb); } @@ -213,6 +359,8 @@ void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } /*########################################################################################################################* *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ +static cc_bool skipRendering; + void Gfx_SetFog(cc_bool enabled) { } @@ -228,11 +376,17 @@ void Gfx_SetFogEnd(float value) { void Gfx_SetFogMode(FogFunc func) { } -void Gfx_SetTexturing(cc_bool enabled) { } +void Gfx_SetAlphaTest(cc_bool enabled) { + if (enabled) { + //glEnable(GL_ALPHA_TEST); + } else { + //glDisable(GL_ALPHA_TEST); + } +} -void Gfx_SetAlphaTest(cc_bool enabled) { } - -void Gfx_DepthOnlyRendering(cc_bool depthOnly) { } +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + skipRendering = depthOnly; +} /*########################################################################################################################* @@ -252,6 +406,13 @@ void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { m.m[i] = floattof32(src[i]); } glLoadMatrix4x4(&m); + + // Vertex commands are signed 16 bit values, with 12 bits fractional + // aka only from -8.0 to 8.0 + // That's way too small to be useful, so counteract that by scaling down + // vertices and then scaling up the matrix multiplication + if (type == MATRIX_VIEW) + glScalef32(floattof32(64.0f), floattof32(64.0f), floattof32(64.0f)); } void Gfx_LoadIdentityMatrix(MatrixType type) { @@ -259,13 +420,19 @@ void Gfx_LoadIdentityMatrix(MatrixType type) { glLoadIdentity(); } -static struct Matrix texMatrix = Matrix_IdentityValue; +static struct Matrix texMatrix; void Gfx_EnableTextureOffset(float x, float y) { - texMatrix.row4.x = x; texMatrix.row4.y = y; + texMatrix.row1.x = x; texMatrix.row2.y = y; Gfx_LoadMatrix(2, &texMatrix); + //glTexParameter(0, TEXGEN_NORMAL | GL_TEXTURE_WRAP_S | GL_TEXTURE_WRAP_T); + } -void Gfx_DisableTextureOffset(void) { Gfx_LoadIdentityMatrix(2); } +void Gfx_DisableTextureOffset(void) { + texMatrix.row1.x = 0; texMatrix.row1.y = 0; + Gfx_LoadMatrix(2, &texMatrix); + //glTexParameter(0, TEXGEN_TEXCOORD | GL_TEXTURE_WRAP_S | GL_TEXTURE_WRAP_T); +} /*########################################################################################################################* @@ -274,6 +441,12 @@ void Gfx_DisableTextureOffset(void) { Gfx_LoadIdentityMatrix(2); } void Gfx_SetVertexFormat(VertexFormat fmt) { gfx_format = fmt; gfx_stride = strideSizes[fmt]; + + if (fmt == VERTEX_FORMAT_TEXTURED) { + glEnable(GL_TEXTURE_2D); + } else { + glDisable(GL_TEXTURE_2D); + } } void Gfx_DrawVb_Lines(int verticesCount) { @@ -284,23 +457,39 @@ static void Draw_ColouredTriangles(int verticesCount, int startVertex) { glBegin(GL_QUADS); for (int i = 0; i < verticesCount; i++) { - struct VertexColoured* v = (struct VertexColoured*)gfx_vertices + startVertex + i; + struct DSColouredVertex* v = (struct DSColouredVertex*)gfx_vertices + startVertex + i; - glColor3b(PackedCol_R(v->Col), PackedCol_G(v->Col), PackedCol_B(v->Col)); - glVertex3f(v->x, v->y, v->z); + GFX_COLOR = v->rgb; + GFX_VERTEX16 = v->xy; + GFX_VERTEX16 = v->z; } glEnd(); } static void Draw_TexturedTriangles(int verticesCount, int startVertex) { glBegin(GL_QUADS); - for (int i = 0; i < verticesCount; i++) + int width = 0, height = 0; + glGetInt(GL_GET_TEXTURE_WIDTH, &width); + glGetInt(GL_GET_TEXTURE_HEIGHT, &height); + + // Original code used was + // U = mulf32(v->u, inttof32(width)) + // which behind the scenes expands to + // W = width << 12 + // U = ((int64)v->u * W) >> 12; + // and in this case, the bit shifts can be cancelled out + // to avoid calling __aeabi_lmul to perform the 64 bit multiplication + // therefore the code can be simplified to + // U = v->u * width + + for (int i = 0; i < verticesCount; i++) { - struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + struct DSTexturedVertex* v = (struct DSTexturedVertex*)gfx_vertices + startVertex + i; - glColor3b(PackedCol_R(v->Col), PackedCol_G(v->Col), PackedCol_B(v->Col)); - glVertex3f(v->x, v->y, v->z); - //GX_TexCoord2f32(v->U, v->V); + GFX_COLOR = v->rgb; + GFX_TEX_COORD = TEXTURE_PACK(f32tot16(v->u * width), f32tot16(v->v * height)); + GFX_VERTEX16 = v->xy; + GFX_VERTEX16 = v->z; } glEnd(); } @@ -322,6 +511,7 @@ void Gfx_DrawVb_IndexedTris(int verticesCount) { } void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { + if (skipRendering) return; Draw_TexturedTriangles(verticesCount, startVertex); } #endif diff --git a/src/Graphics_PS1.c b/src/Graphics_PS1.c new file mode 100644 index 000000000..ce21f156d --- /dev/null +++ b/src/Graphics_PS1.c @@ -0,0 +1,738 @@ +#include "Core.h" +#if defined CC_BUILD_PS1 +#include "_GraphicsBase.h" +#include "Errors.h" +#include "Window.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Based off https://github.com/Lameguy64/PSn00bSDK/blob/master/examples/beginner/hello/main.c + + +// Length of the ordering table, i.e. the range Z coordinates can have, 0-15 in +// this case. Larger values will allow for more granularity with depth (useful +// when drawing a complex 3D scene) at the expense of RAM usage and performance. +#define OT_LENGTH 1024 + +// Size of the buffer GPU commands and primitives are written to. If the program +// crashes due to too many primitives being drawn, increase this value. +#define BUFFER_LENGTH 32768 + +typedef struct { + DISPENV disp_env; + DRAWENV draw_env; + + cc_uint32 ot[OT_LENGTH]; + cc_uint8 buffer[BUFFER_LENGTH]; +} RenderBuffer; + +static RenderBuffer buffers[2]; +static cc_uint8* next_packet; +static int active_buffer; +static RenderBuffer* buffer; + +static void OnBufferUpdated(void) { + buffer = &buffers[active_buffer]; + next_packet = buffer->buffer; + ClearOTagR(buffer->ot, OT_LENGTH); +} + +static void SetupContexts(int w, int h, int r, int g, int b) { + SetDefDrawEnv(&buffers[0].draw_env, 0, 0, w, h); + SetDefDispEnv(&buffers[0].disp_env, 0, 0, w, h); + SetDefDrawEnv(&buffers[1].draw_env, 0, h, w, h); + SetDefDispEnv(&buffers[1].disp_env, 0, h, w, h); + + setRGB0(&buffers[0].draw_env, r, g, b); + setRGB0(&buffers[1].draw_env, r, g, b); + buffers[0].draw_env.isbg = 1; + buffers[1].draw_env.isbg = 1; + + active_buffer = 0; + OnBufferUpdated(); +} + +static void FlipBuffers(void) { + DrawSync(0); + VSync(0); + + RenderBuffer* draw_buffer = &buffers[active_buffer]; + RenderBuffer* disp_buffer = &buffers[active_buffer ^ 1]; + + PutDispEnv(&disp_buffer->disp_env); + DrawOTagEnv(&draw_buffer->ot[OT_LENGTH - 1], &draw_buffer->draw_env); + + active_buffer ^= 1; + OnBufferUpdated(); +} + +static void* new_primitive(int size) { + RenderBuffer* buffer = &buffers[active_buffer]; + uint8_t* prim = next_packet; + + next_packet += size; + + assert(next_packet <= &buffer->buffer[BUFFER_LENGTH]); + return (void*)prim; +} + +static GfxResourceID white_square; +void Gfx_RestoreState(void) { + InitDefaultResources(); + + // 2x2 dummy white texture + struct Bitmap bmp; + BitmapCol pixels[4] = { BitmapColor_RGB(255, 0, 0), BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE }; + Bitmap_Init(bmp, 2, 2, pixels); + white_square = Gfx_CreateTexture(&bmp, 0, false); +} + +void Gfx_FreeState(void) { + FreeDefaultResources(); + Gfx_DeleteTexture(&white_square); +} + +void Gfx_Create(void) { + Gfx.MaxTexWidth = 128; + Gfx.MaxTexHeight = 128; + Gfx.Created = true; + + Gfx_RestoreState(); + ResetGraph(0); + + SetupContexts(Window_Main.Width, Window_Main.Height, 63, 0, 127); + SetDispMask(1); + + InitGeom(); + //gte_SetGeomOffset(Window_Main.Width / 2, Window_Main.Height / 2); + // Set screen depth (basically FOV control, W/2 works best) + //gte_SetGeomScreen(Window_Main.Width / 2); +} + +void Gfx_Free(void) { + Gfx_FreeState(); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Textures--------------------------------------------------------* +*#########################################################################################################################*/ +// VRAM can be divided into texture pages +// 32 texture pages total - each page is 64 x 256 +// 10 texture pages are occupied by the doublebuffered display +// 22 texture packs are usable, and are then divided into +// - 4 pages for 256 wide textures, 8 for 128 wide, 10 for 64 +#define TPAGE_START_HOR 5 +#define TPAGES_PER_HALF 16 + +#define TPAGE_WIDTH 64 +#define TPAGE_HEIGHT 256 +#define MAX_TEX_PAGES 22 +static cc_uint8 vram_used[(MAX_TEX_PAGES * TPAGE_HEIGHT) / 8]; + +#define VRAM_SetUsed(line) (vram_used[(line) / 8] |= (1 << ((line) % 8))) +#define VRAM_UnUsed(line) (vram_used[(line) / 8] &= ~(1 << ((line) % 8))) +#define VRAM_IsUsed(line) (vram_used[(line) / 8] & (1 << ((line) % 8))) + +static void VRAM_GetBlockRange(int width, int* beg, int* end) { + if (width >= 256) { + *beg = 0; + *end = 4 * TPAGE_HEIGHT; + } else if (width >= 128) { + *beg = 4 * TPAGE_HEIGHT; + *end = 12 * TPAGE_HEIGHT; + } else { + *beg = 12 * TPAGE_HEIGHT; + *end = 22 * TPAGE_HEIGHT; + } +} + +static cc_bool VRAM_IsRangeFree(int beg, int end) { + for (int i = beg; i < end; i++) + { + if (VRAM_IsUsed(i)) return false; + } + return true; +} + +static int VRAM_FindFreeBlock(int width, int height) { + int beg, end; + VRAM_GetBlockRange(width, &beg, &end); + + // TODO kinda inefficient + for (int i = beg; i < end - height; i++) + { + if (VRAM_IsUsed(i)) continue; + + if (VRAM_IsRangeFree(i, i + height)) return i; + } + return -1; +} + +#define TEXTURES_MAX_COUNT 64 +typedef struct GPUTexture { + cc_uint16 width, height; + cc_uint16 line, tpage; +} GPUTexture; +static GPUTexture textures[TEXTURES_MAX_COUNT]; +static GPUTexture* active_tex; + +#define BGRA8_to_PS1(src) \ + ((src[2] & 0xF8) >> 3) | ((src[1] & 0xF8) << 2) | ((src[0] & 0xF8) << 7) | ((src[3] & 0x80) << 8) + +static void* AllocTextureAt(int i, struct Bitmap* bmp, int rowWidth) { + cc_uint16* tmp = Mem_TryAlloc(bmp->width * bmp->height, 2); + if (!tmp) return NULL; + + for (int y = 0; y < bmp->height; y++) + { + cc_uint32* src = bmp->scan0 + y * rowWidth; + cc_uint16* dst = tmp + y * bmp->width; + + for (int x = 0; x < bmp->width; x++) { + cc_uint8* color = (cc_uint8*)&src[x]; + dst[x] = BGRA8_to_PS1(color); + } + } + + GPUTexture* tex = &textures[i]; + int line = VRAM_FindFreeBlock(bmp->width, bmp->height); + if (line == -1) { Mem_Free(tmp); return NULL; } + + tex->width = bmp->width; + tex->height = bmp->height; + tex->line = line; + + int page = TPAGE_START_HOR + (line / TPAGE_HEIGHT); + // In bottom half of VRAM? Need to offset horizontally again + if (page >= TPAGES_PER_HALF) page += TPAGE_START_HOR; + + int pageX = (page % TPAGES_PER_HALF); + int pageY = (page / TPAGES_PER_HALF); + + for (int i = tex->line; i < tex->line + tex->height; i++) + { + VRAM_SetUsed(i); + } + tex->tpage = (2 << 7) | (pageY << 4) | pageX; + Platform_Log3("%i x %i = %i", &bmp->width, &bmp->height, &line); + Platform_Log3(" at %i (%i, %i)", &page, &pageX, &pageY); + + RECT rect; + rect.x = pageX * TPAGE_WIDTH; + rect.y = pageY * TPAGE_HEIGHT + (line % TPAGE_HEIGHT); + rect.w = bmp->width; + rect.h = bmp->height; + + int RX = rect.x, RY = rect.y; + Platform_Log2(" LOAD AT: %i, %i", &RX, &RY); + LoadImage2(&rect, tmp); + + Mem_Free(tmp); + return tex; +} + +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + for (int i = 0; i < TEXTURES_MAX_COUNT; i++) + { + if (textures[i].width) continue; + return AllocTextureAt(i, bmp, rowWidth); + } + + Platform_LogConst("No room for more textures"); + return NULL; +} + +void Gfx_BindTexture(GfxResourceID texId) { + if (!texId) texId = white_square; + active_tex = (GPUTexture*)texId; +} + +void Gfx_DeleteTexture(GfxResourceID* texId) { + GfxResourceID data = *texId; + if (!data) return; + GPUTexture* tex = (GPUTexture*)data; + + for (int i = tex->line; i < tex->line + tex->height; i++) + { + VRAM_UnUsed(i); + } + tex->width = 0; tex->height = 0; + *texId = NULL; +} + +void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { + // TODO +} + +void Gfx_EnableMipmaps(void) { } +void Gfx_DisableMipmaps(void) { } + + +/*########################################################################################################################* +*------------------------------------------------------State management---------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetFog(cc_bool enabled) { } +void Gfx_SetFogCol(PackedCol col) { } +void Gfx_SetFogDensity(float value) { } +void Gfx_SetFogEnd(float value) { } +void Gfx_SetFogMode(FogFunc func) { } + +void Gfx_SetFaceCulling(cc_bool enabled) { + // TODO +} + +void Gfx_SetAlphaTest(cc_bool enabled) { +} + +void Gfx_SetAlphaBlending(cc_bool enabled) { +} + +void Gfx_SetAlphaArgBlend(cc_bool enabled) { } + +void Gfx_ClearBuffers(GfxBuffers buffers) { +} + +void Gfx_ClearColor(PackedCol color) { + int r = PackedCol_R(color); + int g = PackedCol_G(color); + int b = PackedCol_B(color); + + setRGB0(&buffers[0].draw_env, r, g, b); + setRGB0(&buffers[1].draw_env, r, g, b); +} + +void Gfx_SetDepthTest(cc_bool enabled) { +} + +void Gfx_SetDepthWrite(cc_bool enabled) { + // TODO +} + +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO +} + +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); +} + + +/*########################################################################################################################* +*-------------------------------------------------------Index buffers-----------------------------------------------------* +*#########################################################################################################################*/ +GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { + return (void*)1; +} + +void Gfx_BindIb(GfxResourceID ib) { } +void Gfx_DeleteIb(GfxResourceID* ib) { } + + +/*########################################################################################################################* +*-------------------------------------------------------Vertex buffers----------------------------------------------------* +*#########################################################################################################################*/ +static void* gfx_vertices; + +static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { + return Mem_TryAlloc(count, strideSizes[fmt]); +} + +void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; } + +void Gfx_DeleteVb(GfxResourceID* vb) { + GfxResourceID data = *vb; + if (data) Mem_Free(data); + *vb = 0; +} + +void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { + return vb; +} + +void Gfx_UnlockVb(GfxResourceID vb) { + gfx_vertices = vb; +} + + +static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { + return Mem_TryAlloc(maxVertices, strideSizes[fmt]); +} + +void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); } + +void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { + return vb; +} + +void Gfx_UnlockDynamicVb(GfxResourceID vb) { + gfx_vertices = vb; +} + +void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } + + +/*########################################################################################################################* +*---------------------------------------------------------Matrices--------------------------------------------------------* +*#########################################################################################################################*/ +static struct Matrix _view, _proj, mvp; +#define ToFixed(v) (int)(v * (1 << 12)) + +static void LoadTransformMatrix(struct Matrix* src) { + // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati + MATRIX mtx; + + mtx.t[0] = 0; + mtx.t[1] = 0; + mtx.t[2] = 0; + + //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row1.x, &src->row1.y, &src->row1.z); + //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row2.x, &src->row2.y, &src->row2.z); + //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row3.x, &src->row3.y, &src->row3.z); + //Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row4.x, &src->row4.y, &src->row4.z); + //Platform_LogConst("===="); + + float len1 = Math_SqrtF(src->row1.x + src->row1.y + src->row1.z); + float len2 = Math_SqrtF(src->row2.x + src->row2.y + src->row2.z); + float len3 = Math_SqrtF(src->row3.x + src->row3.y + src->row3.z); + + mtx.m[0][0] = ToFixed(1); + mtx.m[0][1] = 0; + mtx.m[0][2] = 0; + + mtx.m[1][0] = 0; + mtx.m[1][1] = ToFixed(1); + mtx.m[1][2] = 0; + + mtx.m[2][0] = 0; + mtx.m[2][1] = ToFixed(1); + mtx.m[2][2] = 1; + + gte_SetRotMatrix(&mtx); + gte_SetTransMatrix(&mtx); +} + +/*static void LoadTransformMatrix(struct Matrix* src) { + // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati + MATRIX mtx; + + mtx.t[0] = ToFixed(src->row4.x); + mtx.t[1] = ToFixed(src->row4.y); + mtx.t[2] = ToFixed(src->row4.z); + + Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row1.x, &src->row1.y, &src->row1.z); + Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row2.x, &src->row2.y, &src->row2.z); + Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row3.x, &src->row3.y, &src->row3.z); + Platform_Log3("X: %f3, Y: %f3, Z: %f3", &src->row4.x, &src->row4.y, &src->row4.z); + Platform_LogConst("===="); + + float len1 = Math_SqrtF(src->row1.x + src->row1.y + src->row1.z); + float len2 = Math_SqrtF(src->row2.x + src->row2.y + src->row2.z); + float len3 = Math_SqrtF(src->row3.x + src->row3.y + src->row3.z); + + mtx.m[0][0] = ToFixed(src->row1.x / len1); + mtx.m[0][1] = ToFixed(src->row1.y / len1); + mtx.m[0][2] = ToFixed(src->row1.z / len1); + + mtx.m[1][0] = ToFixed(src->row2.x / len2); + mtx.m[1][1] = ToFixed(src->row2.y / len2); + mtx.m[1][2] = ToFixed(src->row2.z / len2); + + mtx.m[2][0] = ToFixed(src->row3.x / len3); + mtx.m[2][1] = ToFixed(src->row3.y / len3); + mtx.m[2][2] = ToFixed(src->row3.z / len3); + + gte_SetRotMatrix(&mtx); + gte_SetTransMatrix(&mtx); +}*/ + +void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { + if (type == MATRIX_VIEW) _view = *matrix; + if (type == MATRIX_PROJECTION) _proj = *matrix; + + Matrix_Mul(&mvp, &_view, &_proj); + LoadTransformMatrix(&mvp); +} + +void Gfx_LoadIdentityMatrix(MatrixType type) { + Gfx_LoadMatrix(type, &Matrix_Identity); +} + +void Gfx_EnableTextureOffset(float x, float y) { + // TODO +} + +void Gfx_DisableTextureOffset(void) { + // TODO +} + +void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { + /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh */ + /* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */ + /* NOTE: This calculation is shared with Direct3D 11 backend */ + *matrix = Matrix_Identity; + + matrix->row1.x = 2.0f / width; + matrix->row2.y = -2.0f / height; + matrix->row3.z = 1.0f / (zNear - zFar); + + matrix->row4.x = -1.0f; + matrix->row4.y = 1.0f; + matrix->row4.z = zNear / (zNear - zFar); +} + +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } +void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { + float zNear = 0.01f; + /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */ + float c = (float)Cotangent(0.5f * fov); + *matrix = Matrix_Identity; + + matrix->row1.x = c / aspect; + matrix->row2.y = c; + matrix->row3.z = zFar / (zNear - zFar); + matrix->row3.w = -1.0f; + matrix->row4.z = (zNear * zFar) / (zNear - zFar); + matrix->row4.w = 0.0f; +} + + +/*########################################################################################################################* +*---------------------------------------------------------Rendering-------------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetVertexFormat(VertexFormat fmt) { + gfx_format = fmt; + gfx_stride = strideSizes[fmt]; +} + +void Gfx_DrawVb_Lines(int verticesCount) { + +} + +static void Transform(Vec3* result, struct VertexTextured* a, const struct Matrix* mat) { + /* a could be pointing to result - therefore can't directly assign X/Y/Z */ + float x = a->x * mat->row1.x + a->y * mat->row2.x + a->z * mat->row3.x + mat->row4.x; + float y = a->x * mat->row1.y + a->y * mat->row2.y + a->z * mat->row3.y + mat->row4.y; + float z = a->x * mat->row1.z + a->y * mat->row2.z + a->z * mat->row3.z + mat->row4.z; + float w = a->x * mat->row1.w + a->y * mat->row2.w + a->z * mat->row3.w + mat->row4.w; + + result->x = (x/w) * (320/2) + (320/2); + result->y = (y/w) * -(240/2) + (240/2); + result->z = (z/w) * OT_LENGTH; +} + +cc_bool VERTEX_LOGGING; +static void DrawColouredQuads(int verticesCount, int startVertex) { + return; + for (int i = 0; i < verticesCount; i += 4) + { + struct VertexColoured* v = (struct VertexColoured*)gfx_vertices + startVertex + i; + + POLY_F4* poly = new_primitive(sizeof(POLY_F4)); + setPolyF4(poly); + + Vec3 coords[4]; + Transform(&coords[0], &v[0], &mvp); + Transform(&coords[1], &v[1], &mvp); + Transform(&coords[2], &v[2], &mvp); + Transform(&coords[3], &v[3], &mvp); + + poly->x0 = coords[1].x; poly->y0 = coords[1].y; + poly->x1 = coords[0].x; poly->y1 = coords[0].y; + poly->x2 = coords[2].x; poly->y2 = coords[2].y; + poly->x3 = coords[3].x; poly->y3 = coords[3].y; + + int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4; + if (p < 0 || p >= OT_LENGTH) continue; + + int X = v[0].x, Y = v[0].y, Z = v[0].z; + //if (VERTEX_LOGGING) Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z); + X = poly->x1; Y = poly->y1, Z = coords[0].z; + + poly->r0 = PackedCol_R(v->Col); + poly->g0 = PackedCol_G(v->Col); + poly->b0 = PackedCol_B(v->Col); + //if (VERTEX_LOGGING) Platform_Log4("OUT: %i, %i, %i (%i)", &X, &Y, &Z, &p); + + // TODO: 2D shouldn't use AddPrim, draws in the wrong way + addPrim(&buffer->ot[p >> 2], poly); + } +} + +static void DrawTexturedQuads(int verticesCount, int startVertex) { + int pageOffset = active_tex->line % TPAGE_HEIGHT; + + for (int i = 0; i < verticesCount; i += 4) + { + struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + + POLY_FT4* poly = new_primitive(sizeof(POLY_FT4)); + setPolyFT4(poly); + poly->tpage = active_tex->tpage; + poly->clut = 0; + + Vec3 coords[4]; + Transform(&coords[0], &v[0], &mvp); + Transform(&coords[1], &v[1], &mvp); + Transform(&coords[2], &v[2], &mvp); + Transform(&coords[3], &v[3], &mvp); + + // TODO & instead of % + poly->x0 = coords[1].x; poly->y0 = coords[1].y; poly->u0 = (int)(v[1].U * active_tex->width) % active_tex->width; poly->v0 = ((int)(v[1].V * active_tex->height) % active_tex->height) + pageOffset; + poly->x1 = coords[0].x; poly->y1 = coords[0].y; poly->u1 = (int)(v[0].U * active_tex->width) % active_tex->width; poly->v1 = ((int)(v[0].V * active_tex->height) % active_tex->height) + pageOffset; + poly->x2 = coords[2].x; poly->y2 = coords[2].y; poly->u2 = (int)(v[2].U * active_tex->width) % active_tex->width; poly->v2 = ((int)(v[2].V * active_tex->height) % active_tex->height) + pageOffset; + poly->x3 = coords[3].x; poly->y3 = coords[3].y; poly->u3 = (int)(v[3].U * active_tex->width) % active_tex->width; poly->v3 = ((int)(v[3].V * active_tex->height) % active_tex->height) + pageOffset; + + //int P = active_tex->height, page = poly->tpage & 0xFF, ll = active_tex->line % TPAGE_HEIGHT; + //Platform_Log4("XYZ: %f3 x %i, %i, %i", &v[0].V, &P, &page, &ll); + int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4; + if (p < 0 || p >= OT_LENGTH) continue; + + int X = v[0].x, Y = v[0].y, Z = v[0].z; + //if (VERTEX_LOGGING) Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z); + X = poly->x1; Y = poly->y1, Z = coords[0].z; + + poly->r0 = PackedCol_R(v->Col); + poly->g0 = PackedCol_G(v->Col); + poly->b0 = PackedCol_B(v->Col); + //if (VERTEX_LOGGING) Platform_Log4("OUT: %i, %i, %i (%i)", &X, &Y, &Z, &p); + + // TODO: 2D shouldn't use AddPrim, draws in the wrong way + addPrim(&buffer->ot[p >> 2], poly); + } +} + +/*static void DrawQuads(int verticesCount, int startVertex) { + for (int i = 0; i < verticesCount; i += 4) + { + struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + + POLY_F4* poly = new_primitive(sizeof(POLY_F4)); + setPolyF4(poly); + + SVECTOR coords[4]; + coords[0].vx = v[0].x; coords[0].vy = v[0].y; coords[0].vz = v[0].z; + coords[1].vx = v[1].x; coords[1].vy = v[1].y; coords[1].vz = v[1].z; + coords[2].vx = v[2].x; coords[2].vy = v[2].y; coords[2].vz = v[1].z; + coords[3].vx = v[3].x; coords[3].vy = v[3].y; coords[3].vz = v[3].z; + + int X = coords[0].vx, Y = coords[0].vy, Z = coords[0].vz; + //Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z); + gte_ldv3(&coords[0], &coords[1], &coords[2]); + gte_rtpt(); + gte_stsxy0(&poly->x0); + + int p; + gte_avsz3(); + gte_stotz( &p ); + + X = poly->x0; Y = poly->y0, Z = p; + //Platform_Log3("OUT: %i, %i, %i", &X, &Y, &Z); + if (((p >> 2) >= OT_LENGTH) || ((p >> 2) < 0)) + continue; + + gte_ldv0(&coords[3]); + gte_rtps(); + gte_stsxy3(&poly->x1, &poly->x2, &poly->x3); + + //poly->x0 = v[1].x; poly->y0 = v[1].y; + //poly->x1 = v[0].x; poly->y1 = v[0].y; + //poly->x2 = v[2].x; poly->y2 = v[2].y; + //poly->x3 = v[3].x; poly->y3 = v[3].y; + + poly->r0 = PackedCol_R(v->Col); + poly->g0 = PackedCol_G(v->Col); + poly->b0 = PackedCol_B(v->Col); + + addPrim(&buffer->ot[p >> 2], poly); + } +}*/ + +/*static void DrawQuads(int verticesCount, int startVertex) { + for (int i = 0; i < verticesCount; i += 4) + { + struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + + POLY_F4* poly = new_primitive(sizeof(POLY_F4)); + setPolyF4(poly); + + poly->x0 = v[1].x; poly->y0 = v[1].y; + poly->x1 = v[0].x; poly->y1 = v[0].y; + poly->x2 = v[2].x; poly->y2 = v[2].y; + poly->x3 = v[3].x; poly->y3 = v[3].y; + + poly->r0 = PackedCol_R(v->Col); + poly->g0 = PackedCol_G(v->Col); + poly->b0 = PackedCol_B(v->Col); + + int p = 0; + addPrim(&buffer->ot[p >> 2], poly); + } +}*/ + +void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + DrawTexturedQuads(verticesCount, startVertex); + } else { + DrawColouredQuads(verticesCount, startVertex); + } +} + +void Gfx_DrawVb_IndexedTris(int verticesCount) { + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + DrawTexturedQuads(verticesCount, 0); + } else { + DrawColouredQuads(verticesCount, 0); + } +} + +void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { + DrawTexturedQuads(verticesCount, startVertex); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Other/Misc------------------------------------------------------* +*#########################################################################################################################*/ +cc_result Gfx_TakeScreenshot(struct Stream* output) { + return ERR_NOT_SUPPORTED; +} + +cc_bool Gfx_WarnIfNecessary(void) { + return false; +} + +void Gfx_BeginFrame(void) { +} + +void Gfx_EndFrame(void) { + FlipBuffers(); + if (gfx_minFrameMs) LimitFPS(); +} + +void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { + gfx_minFrameMs = minFrameMs; + gfx_vsync = vsync; +} + +void Gfx_OnWindowResize(void) { + // TODO +} + +void Gfx_SetViewport(int x, int y, int w, int h) { } + +void Gfx_GetApiInfo(cc_string* info) { + String_AppendConst(info, "-- Using PS1 --\n"); + PrintMaxTextureInfo(info); +} + +cc_bool Gfx_TryRestoreContext(void) { return true; } +#endif diff --git a/src/Graphics_PS2.c b/src/Graphics_PS2.c index 429619bfe..383bd8677 100644 --- a/src/Graphics_PS2.c +++ b/src/Graphics_PS2.c @@ -171,7 +171,7 @@ typedef struct CCTexture_ { cc_uint32 pixels[]; // aligned to 64 bytes (only need 16?) } CCTexture; -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { int size = bmp->width * bmp->height * 4; CCTexture* tex = (CCTexture*)memalign(16, 64 + size); @@ -180,7 +180,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo tex->log2_width = draw_log2(bmp->width); tex->log2_height = draw_log2(bmp->height); - Mem_Copy(tex->pixels, bmp->scan0, size); + CopyTextureData(tex->pixels, bmp->width * 4, bmp, rowWidth << 2); return tex; } @@ -198,10 +198,6 @@ static int BINDS; void Gfx_BindTexture(GfxResourceID texId) { if (!texId) texId = white_square; CCTexture* tex = (CCTexture*)texId; - Platform_Log2("BIND: %i x %i", &tex->width, &tex->height); - // TODO - if (BINDS) return; - BINDS = 1; texbuffer_t texbuf; texbuf.width = max(256, tex->width); @@ -209,9 +205,8 @@ void Gfx_BindTexture(GfxResourceID texId) { // TODO terrible perf DMATAG_END(dma_tag, (q - current->data) - 1, 0, 0, 0); - dma_wait_fast(); dma_channel_send_chain(DMA_CHANNEL_GIF, current->data, q - current->data, 0, 0); - // + dma_wait_fast(); packet_t *packet = packet_init(200, PACKET_NORMAL); @@ -223,13 +218,11 @@ void Gfx_BindTexture(GfxResourceID texId) { dma_channel_send_chain(DMA_CHANNEL_GIF,packet->data, Q - packet->data, 0,0); dma_wait_fast(); - //packet_free(packet); + packet_free(packet); // TODO terrible perf q = dma_tag + 1; UpdateTextureBuffer(0, &texbuf, tex); - - Platform_LogConst("====="); } void Gfx_DeleteTexture(GfxResourceID* texId) { @@ -242,11 +235,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i // TODO } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - // TODO -} - -void Gfx_SetTexturing(cc_bool enabled) { } void Gfx_EnableMipmaps(void) { } void Gfx_DisableMipmaps(void) { } @@ -298,14 +286,15 @@ void Gfx_SetAlphaBlending(cc_bool enabled) { void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO clear only some buffers q = draw_disable_tests(q, 0, &fb_depth); q = draw_clear(q, 0, 2048.0f - fb_color.width / 2.0f, 2048.0f - fb_color.height / 2.0f, fb_color.width, fb_color.height, clearR, clearG, clearB); UpdateState(0); } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { clearR = PackedCol_R(color); clearG = PackedCol_G(color); clearB = PackedCol_B(color); @@ -320,10 +309,14 @@ void Gfx_SetDepthWrite(cc_bool enabled) { // TODO } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { } +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO +} void Gfx_DepthOnlyRendering(cc_bool depthOnly) { - // TODO + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } @@ -419,11 +412,11 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear_ = zFar; float zFar_ = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */ /* For pos FOV based perspective matrix, left/right/top/bottom are calculated as: */ @@ -678,6 +671,8 @@ void Gfx_OnWindowResize(void) { // TODO } +void Gfx_SetViewport(int x, int y, int w, int h) { } + void Gfx_GetApiInfo(cc_string* info) { String_AppendConst(info, "-- Using PS2 --\n"); PrintMaxTextureInfo(info); diff --git a/src/Graphics_PS3.c b/src/Graphics_PS3.c index 1df69f5e3..3283ba3dc 100644 --- a/src/Graphics_PS3.c +++ b/src/Graphics_PS3.c @@ -291,7 +291,7 @@ void Gfx_SetAlphaBlending(cc_bool enabled) { } void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { cc_uint32 R = PackedCol_R(color); cc_uint32 G = PackedCol_G(color); cc_uint32 B = PackedCol_B(color); @@ -299,16 +299,6 @@ void Gfx_ClearCol(PackedCol color) { clearColor = B | (G << 8) | (R << 16) | (0xFF << 24); } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { - unsigned mask = 0; - if (r) mask |= GCM_COLOR_MASK_R; - if (g) mask |= GCM_COLOR_MASK_G; - if (b) mask |= GCM_COLOR_MASK_B; - if (a) mask |= GCM_COLOR_MASK_A; - - rsxSetColorMask(context, mask); -} - static cc_bool depth_write = true, depth_test = true; static void UpdateDepthState(void) { // match Desktop behaviour, where disabling depth testing also disables depth writing @@ -326,15 +316,24 @@ void Gfx_SetDepthTest(cc_bool enabled) { UpdateDepthState(); } -void Gfx_SetTexturing(cc_bool enabled) { } - void Gfx_SetAlphaTest(cc_bool enabled) { rsxSetAlphaTestEnable(context, enabled); } +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + unsigned mask = 0; + if (r) mask |= GCM_COLOR_MASK_R; + if (g) mask |= GCM_COLOR_MASK_G; + if (b) mask |= GCM_COLOR_MASK_B; + if (a) mask |= GCM_COLOR_MASK_A; + + rsxSetColorMask(context, mask); +} + void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } @@ -355,10 +354,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = zNear / (zNear - zFar); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); // Same as Direct3D9 // TODO: should it be like OpenGL? ??? @@ -415,7 +414,7 @@ static void ResetFrameState(void) { GCM_USER_CLIP_PLANE_DISABLE); // NOTE: Must be called each frame, otherwise renders upside down at 4x zoom - Gfx_OnWindowResize(); + Gfx_SetViewport(0, 0, Game.Width, Game.Height); } // https://github.com/ps3dev/PSL1GHT/blob/master/ppu/include/rsx/rsx.h#L30 @@ -431,9 +430,12 @@ void Gfx_BeginFrame(void) { gcmResetFlipStatus(); } -void Gfx_Clear(void) { - rsxClearSurface(context, GCM_CLEAR_R | GCM_CLEAR_G | GCM_CLEAR_B | GCM_CLEAR_A - | GCM_CLEAR_S | GCM_CLEAR_Z); +void Gfx_ClearBuffers(GfxBuffers buffers) { + int targets = 0; + if (buffers & GFX_BUFFER_COLOR) targets |= (GCM_CLEAR_R | GCM_CLEAR_G | GCM_CLEAR_B | GCM_CLEAR_A); + if (buffers & GFX_BUFFER_DEPTH) targets |= (GCM_CLEAR_S | GCM_CLEAR_Z); + + rsxClearSurface(context, targets); } void Gfx_EndFrame(void) { @@ -448,24 +450,26 @@ void Gfx_EndFrame(void) { } void Gfx_OnWindowResize(void) { + Gfx_SetViewport(0, 0, Game.Width, Game.Height); +} + +void Gfx_SetViewport(int x, int y, int w, int h) { f32 scale[4], offset[4]; - - u16 w = DisplayInfo.Width; - u16 h = DisplayInfo.Height; f32 zmin = 0.0f; f32 zmax = 1.0f; + y = Game.Height - y - h; scale[0] = w * 0.5f; scale[1] = h * -0.5f; scale[2] = (zmax - zmin) * 0.5f; scale[3] = 0.0f; - offset[0] = w * 0.5f; - offset[1] = h * 0.5f; + offset[0] = x + w * 0.5f; + offset[1] = x + h * 0.5f; offset[2] = (zmax + zmin) * 0.5f; offset[3] = 0.0f; - rsxSetViewport(context, 0, 0, w, h, zmin, zmax, scale, offset); - rsxSetScissor(context, 0, 0, w, h); + rsxSetViewport(context, x, y, w, h, zmin, zmax, scale, offset); + rsxSetScissor(context, x, y, w, h); // TODO: even needed? for (int i = 0; i < 8; i++) @@ -559,13 +563,13 @@ typedef struct CCTexture_ { cc_uint32 pixels[]; } CCTexture; -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { int size = bmp->width * bmp->height * 4; CCTexture* tex = (CCTexture*)rsxMemalign(128, 128 + size); tex->width = bmp->width; tex->height = bmp->height; - Mem_Copy(tex->pixels, bmp->scan0, size); + CopyTextureData(tex->pixels, bmp->width * 4, bmp, rowWidth << 2); return tex; } @@ -623,10 +627,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i /* TODO */ } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_EnableMipmaps(void) { } void Gfx_DisableMipmaps(void) { } @@ -709,4 +709,4 @@ void Gfx_DrawVb_IndexedTris(int verticesCount) {/* TODO */ void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {/* TODO */ rsxDrawVertexArray(context, GCM_TYPE_QUADS, startVertex, verticesCount); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_PSP.c b/src/Graphics_PSP.c index 5513c8bd4..82b53a93e 100644 --- a/src/Graphics_PSP.c +++ b/src/Graphics_PSP.c @@ -44,7 +44,7 @@ static void guInit(void) { sceGuOffset(2048 - (SCREEN_WIDTH / 2), 2048 - (SCREEN_HEIGHT / 2)); sceGuViewport(2048, 2048, SCREEN_WIDTH, SCREEN_HEIGHT); sceGuDepthRange(65535,0); - sceGuFrontFace(GU_CW); + sceGuFrontFace(GU_CCW); sceGuShadeModel(GU_SMOOTH); sceGuDisable(GU_TEXTURE_2D); @@ -113,13 +113,13 @@ typedef struct CCTexture_ { cc_uint32 pixels[]; } CCTexture; -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { int size = bmp->width * bmp->height * 4; CCTexture* tex = (CCTexture*)memalign(16, 16 + size); tex->width = bmp->width; tex->height = bmp->height; - Mem_Copy(tex->pixels, bmp->scan0, size); + CopyTextureData(tex->pixels, bmp->width * 4, bmp, rowWidth << 2); return tex; } @@ -140,10 +140,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i } }*/ -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_DeleteTexture(GfxResourceID* texId) { GfxResourceID data = *texId; if (data) Mem_Free(data); @@ -166,17 +162,17 @@ void Gfx_BindTexture(GfxResourceID texId) { *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ static PackedCol gfx_clearColor; -void Gfx_SetFaceCulling(cc_bool enabled) { /*GU_Toggle(GU_CULL_FACE); */ } // TODO: Fix? GU_CCW instead?? +void Gfx_SetFaceCulling(cc_bool enabled) { GU_Toggle(GU_CULL_FACE); } void Gfx_SetAlphaBlending(cc_bool enabled) { GU_Toggle(GU_BLEND); } void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { if (color == gfx_clearColor) return; sceGuClearColor(color); gfx_clearColor = color; } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { unsigned int mask = 0xffffffff; if (r) mask &= 0xffffff00; if (g) mask &= 0xffff00ff; @@ -209,10 +205,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); // Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum // For a FOV based perspective matrix, left/right/top/bottom are calculated as: @@ -233,8 +229,25 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ +static BitmapCol* PSP_GetRow(struct Bitmap* bmp, int y, void* ctx) { + cc_uint8* fb = (cc_uint8*)ctx; + return (BitmapCol*)(fb + y * BUFFER_WIDTH * 4); +} + cc_result Gfx_TakeScreenshot(struct Stream* output) { - return ERR_NOT_SUPPORTED; + int fbWidth, fbFormat; + void* fb; + + int res = sceDisplayGetFrameBuf(&fb, &fbWidth, &fbFormat, PSP_DISPLAY_SETBUF_NEXTFRAME); + if (res < 0) return res; + if (!fb) return ERR_NOT_SUPPORTED; + + struct Bitmap bmp; + bmp.scan0 = NULL; + bmp.width = SCREEN_WIDTH; + bmp.height = SCREEN_HEIGHT; + + return Png_Encode(&bmp, output, PSP_GetRow, false, fb); } void Gfx_GetApiInfo(cc_string* info) { @@ -250,7 +263,14 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { void Gfx_BeginFrame(void) { sceGuStart(GU_DIRECT, list); } -void Gfx_Clear(void) { sceGuClear(GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT); } + +void Gfx_ClearBuffers(GfxBuffers buffers) { + int targets = 0; + if (buffers & GFX_BUFFER_COLOR) targets |= GU_COLOR_BUFFER_BIT; + if (buffers & GFX_BUFFER_DEPTH) targets |= GU_DEPTH_BUFFER_BIT; + + sceGuClear(targets); +} void Gfx_EndFrame(void) { sceGuFinish(); @@ -263,6 +283,8 @@ void Gfx_EndFrame(void) { void Gfx_OnWindowResize(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { } + static cc_uint8* gfx_vertices; static int gfx_fields; @@ -359,7 +381,8 @@ void Gfx_SetAlphaTest(cc_bool enabled) { GU_Toggle(GU_ALPHA_TEST); } void Gfx_DepthOnlyRendering(cc_bool depthOnly) { cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); } diff --git a/src/Graphics_PSVita.c b/src/Graphics_PSVita.c index 57438edb4..c96a799b6 100644 --- a/src/Graphics_PSVita.c +++ b/src/Graphics_PSVita.c @@ -563,8 +563,9 @@ void Gfx_Create(void) { if (!Gfx.Created) InitGPU(); in_scene = false; - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; Gfx.Created = true; gfx_vsync = true; @@ -624,15 +625,11 @@ static void GPUTexture_Unref(GfxResourceID* resource) { struct GPUTexture* tex = (struct GPUTexture*)(*resource); if (!tex) return; *resource = NULL; - - cc_uintptr addr = tex; - Platform_Log1("TEX UNREF %h", &addr); + LinkedList_Append(tex, del_textures_head, del_textures_tail); } static void GPUTexture_Free(struct GPUTexture* tex) { - cc_uintptr addr = tex; - Platform_Log1("TEX DELETE %h", &addr); FreeGPUMemory(tex->uid); Mem_Free(tex); } @@ -672,10 +669,10 @@ static void GPUTextures_DeleteUnreferenced(void) { /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { int size = bmp->width * bmp->height * 4; struct GPUTexture* tex = GPUTexture_Alloc(size); - Mem_Copy(tex->data, bmp->scan0, size); + CopyTextureData(tex->data, bmp->width * 4, bmp, rowWidth << 2); sceGxmTextureInitLinear(&tex->texture, tex->data, SCE_GXM_TEXTURE_FORMAT_A8B8G8R8, bmp->width, bmp->height, 0); @@ -697,10 +694,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i //sceKernelDcacheWritebackInvalidateRange(dst, (tex->width * part->height) * 4); } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_DeleteTexture(GfxResourceID* texId) { GPUTexture_Unref(texId); } @@ -735,10 +728,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); // Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum // For a FOV based perspective matrix, left/right/top/bottom are calculated as: @@ -818,6 +811,8 @@ void Gfx_EndFrame(void) { void Gfx_OnWindowResize(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { } + /*########################################################################################################################* *--------------------------------------------------------GPU Buffers------------------------------------------------------* @@ -838,9 +833,6 @@ struct GPUBuffer* GPUBuffer_Alloc(int size) { buffer->data = AllocGPUMemory(size, SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, SCE_GXM_MEMORY_ATTRIB_READ, &buffer->uid); - - cc_uintptr addr = buffer->data; - Platform_Log2("VB ALLOC %h = %i bytes", &addr, &size); return buffer; } @@ -851,14 +843,10 @@ static void GPUBuffer_Unref(GfxResourceID* resource) { if (!buf) return; *resource = NULL; - cc_uintptr addr = buf; - Platform_Log1("VB UNREF %h", &addr); LinkedList_Append(buf, del_buffers_head, del_buffers_tail); } static void GPUBuffer_Free(struct GPUBuffer* buf) { - cc_uintptr addr = buf; - Platform_Log1("VB DELETE %h", &addr); FreeGPUMemory(buf->uid); Mem_Free(buf); } @@ -1000,11 +988,11 @@ void Gfx_SetFaceCulling(cc_bool enabled) { void Gfx_SetAlphaArgBlend(cc_bool enabled) { } static PackedCol clear_color; -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { clear_color = color; } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { // TODO } @@ -1117,7 +1105,8 @@ void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO clear only some buffers static struct GPUBuffer* clearVB; if (!clearVB) { clearVB = GPUBuffer_Alloc(4 * sizeof(struct VertexColoured)); diff --git a/src/Graphics_Saturn.c b/src/Graphics_Saturn.c new file mode 100644 index 000000000..0949fde19 --- /dev/null +++ b/src/Graphics_Saturn.c @@ -0,0 +1,446 @@ +#include "Core.h" +#if defined CC_BUILD_SATURN +#include "_GraphicsBase.h" +#include "Errors.h" +#include "Window.h" +#include +#include +#include +#include + + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 224 + +#define PRIMITIVE_DRAW_MODE_NORMAL (0) +#define PRIMITIVE_DRAW_MODE_MESH (1) +#define PRIMITIVE_DRAW_MODE_SHADOW (2) +#define PRIMITIVE_DRAW_MODE_HALF_LUMINANCE (3) +#define PRIMITIVE_DRAW_MODE_HALF_TRANSPARENT (4) +#define PRIMITIVE_DRAW_MODE_GOURAUD_SHADING (5) +#define PRIMITIVE_DRAW_MODE_GOURAUD_HALF_LUM (6) +#define PRIMITIVE_DRAW_MODE_GOURAUD_HALF_TRANS (7) +#define PRIMITIVE_DRAW_MODE_COUNT (8) + +#define PRIMITIVE_COLOR RGB1555(1, 31, 0, 31) + +#define CMDS_COUNT 400 + +static PackedCol clear_color; +static vdp1_cmdt_t cmdts_all[CMDS_COUNT]; +static int cmdts_count; +static vdp1_vram_partitions_t _vdp1_vram_partitions; + +static vdp1_cmdt_t* NextPrimitive(void) { + if (cmdts_count >= CMDS_COUNT) Logger_Abort("Too many VDP1 commands"); + return &cmdts_all[cmdts_count++]; +} + +static vdp1_cmdt_draw_mode_t _primitive_draw_mode = { + .raw = 0x0000 +}; + +static int16_vec2_t clear_points[4]; + + +// TODO: how to use VDP1 erase ?? +static void UpdateVDP1Env(void) { + vdp1_env_t env; + vdp1_env_default_init(&env); + + int R = PackedCol_R(clear_color); + int G = PackedCol_G(clear_color); + int B = PackedCol_B(clear_color); + env.erase_color = RGB1555(1, R >> 3, G >> 3, B >> 3); + + vdp1_env_set(&env); +} + +// TODO: should be SCREEN_WIDTH/2 instead ? +static void _primitive_init(void) { + clear_points[0].x = 0; + clear_points[0].y = SCREEN_WIDTH - 1; + + clear_points[1].x = SCREEN_WIDTH - 1; + clear_points[1].y = SCREEN_HEIGHT - 1; + + clear_points[2].x = SCREEN_HEIGHT - 1; + clear_points[2].y = 0; + + clear_points[3].x = 0; + clear_points[3].y = 0; +} + +static GfxResourceID white_square; +void Gfx_RestoreState(void) { + InitDefaultResources(); + + // 2x2 dummy white texture + struct Bitmap bmp; + BitmapCol pixels[4] = { BitmapColor_RGB(255, 0, 0), BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE }; + Bitmap_Init(bmp, 2, 2, pixels); + white_square = Gfx_CreateTexture(&bmp, 0, false); +} + +void Gfx_FreeState(void) { + FreeDefaultResources(); + Gfx_DeleteTexture(&white_square); +} + +void Gfx_Create(void) { + if (!Gfx.Created) { + vdp1_vram_partitions_get(&_vdp1_vram_partitions); +// TODO less ram for gourad base + vdp2_scrn_back_color_set(VDP2_VRAM_ADDR(3, 0x01FFFE), + RGB1555(1, 0, 3, 15)); + vdp2_sprite_priority_set(0, 6); + + UpdateVDP1Env(); + _primitive_init(); + } + + Gfx.MaxTexWidth = 128; + Gfx.MaxTexHeight = 128; + Gfx.Created = true; +} + +void Gfx_Free(void) { + Gfx_FreeState(); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Textures--------------------------------------------------------* +*#########################################################################################################################*/ +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + return NULL; +} + +void Gfx_BindTexture(GfxResourceID texId) { +} + +void Gfx_DeleteTexture(GfxResourceID* texId) { +} + +void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { + // TODO +} + +void Gfx_EnableMipmaps(void) { } +void Gfx_DisableMipmaps(void) { } + + +/*########################################################################################################################* +*------------------------------------------------------State management---------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetFog(cc_bool enabled) { } +void Gfx_SetFogCol(PackedCol col) { } +void Gfx_SetFogDensity(float value) { } +void Gfx_SetFogEnd(float value) { } +void Gfx_SetFogMode(FogFunc func) { } + +void Gfx_SetFaceCulling(cc_bool enabled) { + // TODO +} + +void Gfx_SetAlphaTest(cc_bool enabled) { +} + +void Gfx_SetAlphaBlending(cc_bool enabled) { +} + +void Gfx_SetAlphaArgBlend(cc_bool enabled) { } + +void Gfx_ClearBuffers(GfxBuffers buffers) { +} + +void Gfx_ClearColor(PackedCol color) { + if (color == clear_color) return; + + clear_color = color; + UpdateVDP1Env(); +} + +void Gfx_SetDepthTest(cc_bool enabled) { +} + +void Gfx_SetDepthWrite(cc_bool enabled) { + // TODO +} + +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO +} + +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); +} + + +/*########################################################################################################################* +*-------------------------------------------------------Index buffers-----------------------------------------------------* +*#########################################################################################################################*/ +GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { + return (void*)1; +} + +void Gfx_BindIb(GfxResourceID ib) { } +void Gfx_DeleteIb(GfxResourceID* ib) { } + + +/*########################################################################################################################* +*-------------------------------------------------------Vertex buffers----------------------------------------------------* +*#########################################################################################################################*/ +static void* gfx_vertices; + +static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { + return Mem_TryAlloc(count, strideSizes[fmt]); +} + +void Gfx_BindVb(GfxResourceID vb) { gfx_vertices = vb; } + +void Gfx_DeleteVb(GfxResourceID* vb) { + GfxResourceID data = *vb; + if (data) Mem_Free(data); + *vb = 0; +} + +void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { + return vb; +} + +void Gfx_UnlockVb(GfxResourceID vb) { + gfx_vertices = vb; +} + + +static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { + return Mem_TryAlloc(maxVertices, strideSizes[fmt]); +} + +void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); } + +void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { + return vb; +} + +void Gfx_UnlockDynamicVb(GfxResourceID vb) { + gfx_vertices = vb; +} + +void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } + + +/*########################################################################################################################* +*---------------------------------------------------------Matrices--------------------------------------------------------* +*#########################################################################################################################*/ +static struct Matrix _view, _proj, mvp; + +void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { + if (type == MATRIX_VIEW) _view = *matrix; + if (type == MATRIX_PROJECTION) _proj = *matrix; + + Matrix_Mul(&mvp, &_view, &_proj); +} + +void Gfx_LoadIdentityMatrix(MatrixType type) { + Gfx_LoadMatrix(type, &Matrix_Identity); +} + +void Gfx_EnableTextureOffset(float x, float y) { + // TODO +} + +void Gfx_DisableTextureOffset(void) { + // TODO +} + +void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { + /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterrh */ + /* The simplified calculation below uses: L = 0, R = width, T = 0, B = height */ + /* NOTE: This calculation is shared with Direct3D 11 backend */ + *matrix = Matrix_Identity; + + matrix->row1.x = 2.0f / width; + matrix->row2.y = -2.0f / height; + matrix->row3.z = 1.0f / (zNear - zFar); + + matrix->row4.x = -1.0f; + matrix->row4.y = 1.0f; + matrix->row4.z = zNear / (zNear - zFar); +} + +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } +void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { + float zNear = 0.01f; + /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */ + float c = Cotangent(0.5f * fov); + *matrix = Matrix_Identity; + + matrix->row1.x = c / aspect; + matrix->row2.y = c; + matrix->row3.z = zFar / (zNear - zFar); + matrix->row3.w = -1.0f; + matrix->row4.z = (zNear * zFar) / (zNear - zFar); + matrix->row4.w = 0.0f; +} + + +/*########################################################################################################################* +*---------------------------------------------------------Rendering-------------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetVertexFormat(VertexFormat fmt) { + gfx_format = fmt; + gfx_stride = strideSizes[fmt]; +} + +void Gfx_DrawVb_Lines(int verticesCount) { + +} + +static void Transform(Vec3* result, struct VertexTextured* a, const struct Matrix* mat) { + /* a could be pointing to result - therefore can't directly assign X/Y/Z */ + float x = a->x * mat->row1.x + a->y * mat->row2.x + a->z * mat->row3.x + mat->row4.x; + float y = a->x * mat->row1.y + a->y * mat->row2.y + a->z * mat->row3.y + mat->row4.y; + float z = a->x * mat->row1.z + a->y * mat->row2.z + a->z * mat->row3.z + mat->row4.z; + float w = a->x * mat->row1.w + a->y * mat->row2.w + a->z * mat->row3.w + mat->row4.w; + + result->x = (x/w) * (320/2); + result->y = (y/w) * -(224/2); + result->z = (z/w) * 1024; +} + +#define IsPointCulled(vec) vec.x < -10000 || vec.x > 10000 || vec.y < -10000 || vec.y > 10000 || vec.z < 0 || vec.z > 1024 + +static void DrawTexturedQuads(int verticesCount, int startVertex) { + for (int i = 0; i < verticesCount; i += 4) + { + struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + + Vec3 coords[4]; + Transform(&coords[0], &v[0], &mvp); + Transform(&coords[1], &v[1], &mvp); + Transform(&coords[2], &v[2], &mvp); + Transform(&coords[3], &v[3], &mvp); + + int16_vec2_t points[4]; + points[0].x = coords[0].x; points[0].y = coords[0].y; + points[1].x = coords[1].x; points[1].y = coords[1].y; + points[2].x = coords[2].x; points[2].y = coords[2].y; + points[3].x = coords[3].x; points[3].y = coords[3].y; + + if (IsPointCulled(coords[0])) continue; + if (IsPointCulled(coords[1])) continue; + if (IsPointCulled(coords[2])) continue; + if (IsPointCulled(coords[3])) continue; + + int R = PackedCol_R(v->Col); + int G = PackedCol_G(v->Col); + int B = PackedCol_B(v->Col); + + vdp1_cmdt_t* cmd; + + cmd = NextPrimitive(); + vdp1_cmdt_polygon_set(cmd); + vdp1_cmdt_color_set(cmd, RGB1555(1, R >> 3, G >> 3, B >> 3)); + vdp1_cmdt_draw_mode_set(cmd, _primitive_draw_mode); + vdp1_cmdt_vtx_set(cmd, points); + } +} + +void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + DrawTexturedQuads(verticesCount, startVertex); + } +} + +void Gfx_DrawVb_IndexedTris(int verticesCount) { + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + DrawTexturedQuads(verticesCount, 0); + } +} + +void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { + DrawTexturedQuads(verticesCount, startVertex); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Other/Misc------------------------------------------------------* +*#########################################################################################################################*/ +cc_result Gfx_TakeScreenshot(struct Stream* output) { + return ERR_NOT_SUPPORTED; +} + +cc_bool Gfx_WarnIfNecessary(void) { + return false; +} + +void Gfx_BeginFrame(void) { + Platform_LogConst("FRAME BEG"); + cmdts_count = 0; + + static const int16_vec2_t system_clip_coord = { SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1 }; + static const int16_vec2_t local_coord_center = { SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 }; + + vdp1_cmdt_t* cmd; + + cmd = NextPrimitive(); + vdp1_cmdt_system_clip_coord_set(cmd); + vdp1_cmdt_vtx_system_clip_coord_set(cmd, system_clip_coord); + + cmd = NextPrimitive(); + vdp1_cmdt_local_coord_set(cmd); + vdp1_cmdt_vtx_local_coord_set(cmd, local_coord_center); + + int R = PackedCol_R(clear_color); + int G = PackedCol_G(clear_color); + int B = PackedCol_B(clear_color); + + cmd = NextPrimitive(); + vdp1_cmdt_polygon_set(cmd); + vdp1_cmdt_color_set(cmd, RGB1555(1, R >> 3, G >> 3, B >> 3)); // TODO VDP1 erase + vdp1_cmdt_draw_mode_set(cmd, _primitive_draw_mode); + vdp1_cmdt_vtx_set(cmd, clear_points); +} + +void Gfx_EndFrame(void) { + Platform_LogConst("FRAME END"); + vdp1_cmdt_t* cmd; + + cmd = NextPrimitive(); + vdp1_cmdt_end_set(cmd); + + vdp1_cmdt_list_t cmdt_list; + cmdt_list.cmdts = cmdts_all; + cmdt_list.count = cmdts_count; + vdp1_sync_cmdt_list_put(&cmdt_list, 0); + + vdp1_sync_render(); + vdp1_sync(); + vdp2_sync(); + vdp2_sync_wait(); + + if (gfx_minFrameMs) LimitFPS(); +} + +void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { + gfx_minFrameMs = minFrameMs; + gfx_vsync = vsync; +} + +void Gfx_OnWindowResize(void) { + // TODO +} + +void Gfx_SetViewport(int x, int y, int w, int h) { } + +void Gfx_GetApiInfo(cc_string* info) { + String_AppendConst(info, "-- Using Saturn --\n"); + PrintMaxTextureInfo(info); +} + +cc_bool Gfx_TryRestoreContext(void) { return true; } +#endif diff --git a/src/Graphics_SoftGPU.c b/src/Graphics_SoftGPU.c index 5c222ca1b..5b19f26fe 100644 --- a/src/Graphics_SoftGPU.c +++ b/src/Graphics_SoftGPU.c @@ -83,13 +83,12 @@ void Gfx_DeleteTexture(GfxResourceID* texId) { *texId = NULL; } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { - int size = bmp->width * bmp->height * 4; +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { CCTexture* tex = (CCTexture*)Mem_Alloc(2 + bmp->width * bmp->height, 4, "Texture"); tex->width = bmp->width; tex->height = bmp->height; - Mem_Copy(tex->pixels, bmp->scan0, size); + CopyTextureData(tex->pixels, bmp->width * 4, bmp, rowWidth << 2); return tex; } @@ -99,11 +98,6 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i CopyTextureData(dst, tex->width * 4, part, rowWidth << 2); } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - -void Gfx_SetTexturing(cc_bool enabled) { } void Gfx_EnableMipmaps(void) { } void Gfx_DisableMipmaps(void) { } @@ -131,14 +125,18 @@ void Gfx_SetAlphaBlending(cc_bool enabled) { void Gfx_SetAlphaArgBlend(cc_bool enabled) { } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { int i, size = width * height; - for (i = 0; i < size; i++) colorBuffer[i] = clearColor; - for (i = 0; i < size; i++) depthBuffer[i] = 1.0f; + if (buffers & GFX_BUFFER_COLOR) { + for (i = 0; i < size; i++) colorBuffer[i] = clearColor; + } + if (buffers & GFX_BUFFER_DEPTH) { + for (i = 0; i < size; i++) depthBuffer[i] = 100000000.0f; + } } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { int R = PackedCol_R(color); int G = PackedCol_G(color); int B = PackedCol_B(color); @@ -155,7 +153,9 @@ void Gfx_SetDepthWrite(cc_bool enabled) { depthWrite = enabled; } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { } +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + // TODO +} void Gfx_DepthOnlyRendering(cc_bool depthOnly) { colWrite = !depthOnly; @@ -255,10 +255,10 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float matrix->row4.z = -(zFar + zNear) / (zFar - zNear); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); /* Transposed, source https://learn.microsoft.com/en-us/windows/win32/opengl/glfrustum */ /* For pos FOV based perspective matrix, left/right/top/bottom are calculated as: */ @@ -278,9 +278,9 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f /*########################################################################################################################* *---------------------------------------------------------Rendering-------------------------------------------------------* *#########################################################################################################################*/ -typedef struct Vector4 { float X, Y, Z, W; } Vector4; -typedef struct Vector3 { float X, Y, Z; } Vector3; -typedef struct Vector2 { float X, Y; } Vector2; +typedef struct Vector4 { float x, y, z, w; } Vector4; +typedef struct Vector3 { float x, y, z; } Vector3; +typedef struct Vector2 { float x, y; } Vector2; static void TransformVertex(int index, Vector4* frag, Vector2* uv, PackedCol* color) { // TODO: avoid the multiply, just add down in DrawTriangles @@ -293,10 +293,11 @@ static void TransformVertex(int index, Vector4* frag, Vector2* uv, PackedCol* co coord.z = pos->x * mvp.row1.z + pos->y * mvp.row2.z + pos->z * mvp.row3.z + mvp.row4.z; coord.w = pos->x * mvp.row1.w + pos->y * mvp.row2.w + pos->z * mvp.row3.w + mvp.row4.w; - frag->x = vp_hwidth * (1 + coord.x / coord.w); - frag->y = vp_hheight * (1 - coord.y / coord.w); - frag->z = coord.z / coord.w; - frag.w = 1.0f / coord.w; + float invW = 1.0f / coord.w; + frag->x = vp_hwidth * (1 + coord.x * invW); + frag->y = vp_hheight * (1 - coord.y * invW); + frag->z = coord.z * invW; + frag->w = invW; if (gfx_format != VERTEX_FORMAT_TEXTURED) { struct VertexColoured* v = (struct VertexColoured*)ptr; @@ -304,8 +305,8 @@ static void TransformVertex(int index, Vector4* frag, Vector2* uv, PackedCol* co } else { struct VertexTextured* v = (struct VertexTextured*)ptr; *color = v->Col; - uv->x = v->U + texOffsetX; - uv->y = v->V + texOffsetY; + uv->x = (v->U + texOffsetX) * invW; + uv->y = (v->V + texOffsetY) * invW; } } @@ -332,43 +333,54 @@ static void DrawTriangle(Vector4 frag1, Vector4 frag2, Vector4 frag3, int maxY = max(y1, max(y2, y3)); // TODO backface culling + if (faceCulling) { + // https://gamedev.stackexchange.com/questions/203694/how-to-make-backface-culling-work-correctly-in-both-orthographic-and-perspective + int sign = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1); + if (sign > 0) return; + } // Reject triangles completely outside if (minX < 0 && maxX < 0 || minX >= width && maxX >= width ) return; if (minY < 0 && maxY < 0 || minY >= height && maxY >= height) return; // Perform scissoring - //minX = max(minX, 0); maxX = min(maxX, sc_maxX); - //minY = max(minY, 0); maxY = min(maxY, sc_maxY); - // TODO why doesn't this work + minX = max(minX, 0); maxX = min(maxX, sc_maxX); + minY = max(minY, 0); maxY = min(maxY, sc_maxY); // NOTE: W in frag variables below is actually 1/W - float factor = 1.0f / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3)); + + // TODO proper clipping + if (frag1.w <= 0 || frag2.w <= 0 || frag3.w <= 0) return; + for (int y = minY; y <= maxY; y++) { + float yy = y + 0.5f; for (int x = minX; x <= maxX; x++) { - if (x < 0 || y < 0 || x >= width || y >= height) return; - - float ic0 = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) * factor; + float xx = x + 0.5f; + + float ic0 = ((y2 - y3) * (xx - x3) + (x3 - x2) * (yy - y3)) * factor; if (ic0 < 0 || ic0 > 1) continue; - float ic1 = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) * factor; + float ic1 = ((y3 - y1) * (xx - x3) + (x1 - x3) * (yy - y3)) * factor; if (ic1 < 0 || ic1 > 1) continue; float ic2 = 1.0f - ic0 - ic1; if (ic2 < 0 || ic2 > 1) continue; int index = y * width + x; float w = 1 / (ic0 * frag1.w + ic1 * frag2.w + ic2 * frag3.w); + float z = (ic0 * frag1.z + ic1 * frag2.z + ic2 * frag3.z) * w; - if (depthTest && w <= depthBuffer[index]) continue; - if (depthWrite) depthBuffer[index] = w; - if (!colWrite) continue; + if (depthTest && (z < 0 || z > depthBuffer[index])) continue; + if (!colWrite) { + if (depthWrite) depthBuffer[index] = z; + continue; + } PackedCol fragColor = color; if (gfx_format == VERTEX_FORMAT_TEXTURED) { - float u = (ic0 * uv1.x * frag1.w + ic1 * uv2.x * frag2.w + ic2 * uv3.x * frag3.w) * w; - float v = (ic0 * uv1.y * frag1.w + ic1 * uv2.y * frag2.w + ic2 * uv3.y * frag3.w) * w; - int texX = (int)(Math_AbsF(u - Math_Floor(u)) * curTexWidth); - int texY = (int)(Math_AbsF(v - Math_Floor(v)) * curTexHeight); + float u = (ic0 * uv1.x + ic1 * uv2.x + ic2 * uv3.x) * w; + float v = (ic0 * uv1.y + ic1 * uv2.y + ic2 * uv3.y) * w; + int texX = ((int)(Math_AbsF(u - Math_Floor(u)) * curTexWidth )) % curTexWidth; // TODO avoid slow % + int texY = ((int)(Math_AbsF(v - Math_Floor(v)) * curTexHeight)) % curTexHeight; int texIndex = texY * curTexWidth + texX; fragColor = MultiplyColours(fragColor, curTexPixels[texIndex]); @@ -391,6 +403,7 @@ static void DrawTriangle(Vector4 frag1, Vector4 frag2, Vector4 frag3, } if (alphaTest && A < 0x80) continue; + if (depthWrite) depthBuffer[index] = z; colorBuffer[index] = BitmapCol_Make(R, G, B, 0xFF); } } @@ -478,6 +491,8 @@ void Gfx_OnWindowResize(void) { colorBuffer = fb_bmp.scan0; } +void Gfx_SetViewport(int x, int y, int w, int h) { } + void Gfx_GetApiInfo(cc_string* info) { int pointerSize = sizeof(void*) * 8; String_Format1(info, "-- Using software (%i bit) --\n", &pointerSize); @@ -485,4 +500,4 @@ void Gfx_GetApiInfo(cc_string* info) { } cc_bool Gfx_TryRestoreContext(void) { return true; } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_WiiU.c b/src/Graphics_WiiU.c new file mode 100644 index 000000000..7cc5991e5 --- /dev/null +++ b/src/Graphics_WiiU.c @@ -0,0 +1,481 @@ +#include "Core.h" +#ifdef CC_BUILD_WIIU +#include "_GraphicsBase.h" +#include "Errors.h" +#include "Window.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../build-wiiu/coloured_gsh.h" +#include "../build-wiiu/textured_gsh.h" + +static WHBGfxShaderGroup colorShader; +static WHBGfxShaderGroup textureShader; +static GX2Sampler sampler; +static GfxResourceID white_square; +static WHBGfxShaderGroup* group; + +static void InitGfx(void) { + GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_WRAP, GX2_TEX_XY_FILTER_MODE_POINT); + + WHBGfxLoadGFDShaderGroup(&colorShader, 0, coloured_gsh); + WHBGfxInitShaderAttribute(&colorShader, "in_pos", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32_32); + WHBGfxInitShaderAttribute(&colorShader, "in_col", 0, 12, GX2_ATTRIB_FORMAT_UNORM_8_8_8_8); + WHBGfxInitFetchShader(&colorShader); + + WHBGfxLoadGFDShaderGroup(&textureShader, 0, textured_gsh); + WHBGfxInitShaderAttribute(&textureShader, "in_pos", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32_32); + WHBGfxInitShaderAttribute(&textureShader, "in_col", 0, 12, GX2_ATTRIB_FORMAT_UNORM_8_8_8_8); + WHBGfxInitShaderAttribute(&textureShader, "in_uv", 0, 16, GX2_ATTRIB_FORMAT_FLOAT_32_32); + WHBGfxInitFetchShader(&textureShader); +} + +void Gfx_Create(void) { + if (!Gfx.Created) InitGfx(); + + Gfx.Created = true; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; +} + +cc_bool Gfx_TryRestoreContext(void) { + return true; +} + +void Gfx_Free(void) { + Gfx_FreeState(); +} + +static void Gfx_FreeState(void) { + FreeDefaultResources(); + Gfx_DeleteTexture(&white_square); +} + +static void Gfx_RestoreState(void) { + Gfx_SetFaceCulling(false); + InitDefaultResources(); + gfx_format = -1; + + // 1x1 dummy white texture + struct Bitmap bmp; + BitmapCol pixels[1] = { BITMAPCOLOR_WHITE }; + Bitmap_Init(bmp, 1, 1, pixels); + white_square = Gfx_CreateTexture(&bmp, 0, false); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Textures--------------------------------------------------------* +*#########################################################################################################################*/ +static GX2Texture* pendingTex; + +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + GX2Texture* tex = Mem_TryAllocCleared(1, sizeof(GX2Texture)); + if (!tex) return NULL; + + // TODO handle out of memory better + int width = bmp->width, height = bmp->height; + tex->surface.width = width; + tex->surface.height = height; + tex->surface.depth = 1; + tex->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D; + tex->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8; + tex->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED; + tex->viewNumSlices = 1; + tex->compMap = GX2_COMP_MAP(GX2_SQ_SEL_R, GX2_SQ_SEL_G, GX2_SQ_SEL_B, GX2_SQ_SEL_A); + GX2CalcSurfaceSizeAndAlignment(&tex->surface); + GX2InitTextureRegs(tex); + + tex->surface.image = MEMAllocFromDefaultHeapEx(tex->surface.imageSize, tex->surface.alignment); + if (!tex->surface.image) { Mem_Free(tex); return NULL; } + + CopyTextureData(tex->surface.image, tex->surface.pitch << 2, bmp, rowWidth << 2); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, tex->surface.image, tex->surface.imageSize); + return tex; +} + +void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { + GX2Texture* tex = (GX2Texture*)texId; + uint32_t* dst = (uint32_t*)tex->surface.image + (y * tex->surface.pitch) + x; + + CopyTextureData(dst, tex->surface.pitch << 2, part, rowWidth << 2); + GX2Invalidate(GX2_INVALIDATE_MODE_CPU_TEXTURE, tex->surface.image, tex->surface.imageSize); +} + +void Gfx_BindTexture(GfxResourceID texId) { + if (!texId) texId = white_square; + pendingTex = (GX2Texture*)texId; + // Texture is bound to active shader, so might need to defer it in + // case a call to Gfx_BindTexture was called even though vertex format wasn't textured + // TODO: Track as dirty uniform flag instead? +} + +static void BindPendingTexture(void) { + if (!pendingTex || group != &textureShader) return; + + GX2SetPixelTexture(pendingTex, group->pixelShader->samplerVars[0].location); + GX2SetPixelSampler(&sampler, group->pixelShader->samplerVars[0].location); + pendingTex = NULL; +} + +void Gfx_DeleteTexture(GfxResourceID* texId) { + if (*texId == pendingTex) pendingTex = NULL; + // TODO free memory ??? +} + +void Gfx_EnableMipmaps(void) { } // TODO + +void Gfx_DisableMipmaps(void) { } // TODO + + +/*########################################################################################################################* +*-----------------------------------------------------State management----------------------------------------------------* +*#########################################################################################################################*/ +static float clearR, clearG, clearB; +static cc_bool depthWrite = true, depthTest = true; + +static void UpdateDepthState(void) { + GX2SetDepthOnlyControl(depthTest, depthWrite, GX2_COMPARE_FUNC_LEQUAL); +} + +void Gfx_SetFaceCulling(cc_bool enabled) { + GX2SetCullOnlyControl(GX2_FRONT_FACE_CCW, false, enabled); +} + +void Gfx_SetFog(cc_bool enabled) { + // TODO +} + +void Gfx_SetFogCol(PackedCol color) { + // TODO +} + +void Gfx_SetFogDensity(float value) { + // TODO +} + +void Gfx_SetFogEnd(float value) { + // TODO +} + +void Gfx_SetFogMode(FogFunc func) { + // TODO +} + +void Gfx_SetAlphaTest(cc_bool enabled) { + GX2SetAlphaTest(enabled, GX2_COMPARE_FUNC_GEQUAL, 0.5f); +} + +void Gfx_SetAlphaBlending(cc_bool enabled) { + GX2SetBlendControl(GX2_RENDER_TARGET_0, + GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD, + true, + GX2_BLEND_MODE_SRC_ALPHA, GX2_BLEND_MODE_INV_SRC_ALPHA, GX2_BLEND_COMBINE_MODE_ADD); + GX2SetColorControl(GX2_LOGIC_OP_COPY, enabled, FALSE, TRUE); +} + +void Gfx_SetAlphaArgBlend(cc_bool enabled) { +} + +void Gfx_ClearColor(PackedCol color) { + clearR = PackedCol_R(color) / 255.0f; + clearG = PackedCol_G(color) / 255.0f; + clearB = PackedCol_B(color) / 255.0f; +} + +void Gfx_SetDepthTest(cc_bool enabled) { + depthTest = enabled; + UpdateDepthState(); +} + +void Gfx_SetDepthWrite(cc_bool enabled) { + depthWrite = enabled; + UpdateDepthState(); +} + +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { + GX2ChannelMask mask = 0; + if (r) mask |= GX2_CHANNEL_MASK_R; + if (g) mask |= GX2_CHANNEL_MASK_G; + if (b) mask |= GX2_CHANNEL_MASK_B; + if (a) mask |= GX2_CHANNEL_MASK_A; + + // TODO: use GX2SetColorControl to disable all writing ??? + GX2SetTargetChannelMasks(mask, 0,0,0, 0,0,0,0); +} + +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); +} + + +/*########################################################################################################################* +*-------------------------------------------------------Index buffers-----------------------------------------------------* +*#########################################################################################################################*/ +GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { + return (void*)1; // don't need index buffers.. ? +} + +void Gfx_BindIb(GfxResourceID ib) { +} + +void Gfx_DeleteIb(GfxResourceID* ib) { +} + + +/*########################################################################################################################* +*------------------------------------------------------Vertex buffers-----------------------------------------------------* +*#########################################################################################################################*/ +static GfxResourceID Gfx_AllocStaticVb(VertexFormat fmt, int count) { + GX2RBuffer* buf = Mem_TryAllocCleared(1, sizeof(GX2RBuffer)); + if (!buf) return NULL; + + buf->flags = GX2R_RESOURCE_BIND_VERTEX_BUFFER | GX2R_RESOURCE_USAGE_CPU_READ | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ; + buf->elemSize = strideSizes[fmt]; + buf->elemCount = count; + + if (GX2RCreateBuffer(buf)) return buf; + // Something went wrong ?? TODO + Mem_Free(buf); + return NULL; +} + +void Gfx_DeleteVb(GfxResourceID* vb) { + GX2RBuffer* buf = *vb; + if (!buf) return; + + GX2RDestroyBufferEx(buf, 0); + Mem_Free(buf); + *vb = NULL; +} + +void Gfx_BindVb(GfxResourceID vb) { + GX2RBuffer* buf = (GX2RBuffer*)vb; + GX2RSetAttributeBuffer(buf, 0, buf->elemSize, 0); + //GX2SetAttribBuffer(0, +} + +void* Gfx_LockVb(GfxResourceID vb, VertexFormat fmt, int count) { + GX2RBuffer* buf = (GX2RBuffer*)vb; + return GX2RLockBufferEx(buf, 0); +} + +void Gfx_UnlockVb(GfxResourceID vb) { + GX2RBuffer* buf = (GX2RBuffer*)vb; + GX2RUnlockBufferEx(buf, 0); +} + + +/*########################################################################################################################* +*--------------------------------------------------Dynamic vertex buffers-------------------------------------------------* +*#########################################################################################################################*/ +static GfxResourceID Gfx_AllocDynamicVb(VertexFormat fmt, int maxVertices) { + return Gfx_AllocStaticVb(fmt, maxVertices); +} + +void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); } + +void Gfx_BindDynamicVb(GfxResourceID vb) { Gfx_BindVb(vb); } + +void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { + return Gfx_LockVb(vb, fmt, count); +} + +void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_UnlockVb(vb); Gfx_BindVb(vb); } + + +/*########################################################################################################################* +*-----------------------------------------------------Vertex rendering----------------------------------------------------* +*#########################################################################################################################*/ +void Gfx_SetVertexFormat(VertexFormat fmt) { + if (fmt == gfx_format) return; + gfx_format = fmt; + gfx_stride = strideSizes[fmt]; + + group = fmt == VERTEX_FORMAT_TEXTURED ? &textureShader : &colorShader; + GX2SetFetchShader(&group->fetchShader); + GX2SetVertexShader(group->vertexShader); + GX2SetPixelShader(group->pixelShader); +} + +void Gfx_DrawVb_Lines(int verticesCount) { + BindPendingTexture(); + GX2DrawEx(GX2_PRIMITIVE_MODE_LINES, verticesCount, 0, 1); +} + +void Gfx_DrawVb_IndexedTris(int verticesCount) { + BindPendingTexture(); + GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, verticesCount, 0, 1); +} + +void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { + BindPendingTexture(); + GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, verticesCount, startVertex, 1); +} + +void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { + BindPendingTexture(); + GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, verticesCount, startVertex, 1); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Matrices--------------------------------------------------------* +*#########################################################################################################################*/ +static struct Matrix _view, _proj; +void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { + if (type == MATRIX_VIEW) _view = *matrix; + if (type == MATRIX_PROJECTION) _proj = *matrix; + + // TODO dirty uniform + struct Matrix mvp __attribute__((aligned(64))); + Matrix_Mul(&mvp, &_view, &_proj); + if (!group) return; + GX2SetVertexUniformReg(group->vertexShader->uniformVars[0].offset, 16, &mvp); +} + +void Gfx_LoadIdentityMatrix(MatrixType type) { + Gfx_LoadMatrix(type, &Matrix_Identity); +} + +void Gfx_EnableTextureOffset(float x, float y) { + // TODO +} + +void Gfx_DisableTextureOffset(void) { + // TODO +} + +void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float zNear, float zFar) { + // TODO verify this + *matrix = Matrix_Identity; + + matrix->row1.x = 2.0f / width; + matrix->row2.y = -2.0f / height; + matrix->row3.z = 1.0f / (zNear - zFar); + + matrix->row4.x = -1.0f; + matrix->row4.y = 1.0f; + matrix->row4.z = zNear / (zNear - zFar); +} + +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } +void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { + // TODO verify this + float zNear = 0.1f; + float c = Cotangent(0.5f * fov); + *matrix = Matrix_Identity; + + matrix->row1.x = c / aspect; + matrix->row2.y = c; + matrix->row3.z = zFar / (zNear - zFar); + matrix->row3.w = -1.0f; + matrix->row4.z = (zNear * zFar) / (zNear - zFar); + matrix->row4.w = 0.0f; +} + + +/*########################################################################################################################* +*-----------------------------------------------------------Misc----------------------------------------------------------* +*#########################################################################################################################*/ +cc_result Gfx_TakeScreenshot(struct Stream* output) { + return ERR_NOT_SUPPORTED; +} + +void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { + gfx_minFrameMs = minFrameMs; + gfx_vsync = vsync; + + // TODO GX2SetSwapInterval(1); +} + +void Gfx_BeginFrame(void) { + uint32_t swapCount, flipCount; + OSTime lastFlip, lastVsync; + + for (int try = 0; try < 10; try++) + { + GX2GetSwapStatus(&swapCount, &flipCount, &lastFlip, &lastVsync); + if (flipCount >= swapCount) break; + GX2WaitForVsync(); // TODO vsync + } + + GX2ContextState* state = WHBGfxGetTVContextState(); + GX2SetContextState(state); +} + +void Gfx_ClearBuffers(GfxBuffers buffers) { + GX2ColorBuffer* buf = WHBGfxGetTVColourBuffer(); + GX2DepthBuffer* dph = WHBGfxGetTVDepthBuffer(); + + if (buffers & GFX_BUFFER_COLOR) { + GX2ClearColor(buf, clearR, clearG, clearB, 1.0f); + } + if (buffers & GFX_BUFFER_DEPTH) { + GX2ClearDepthStencilEx(dph, 1.0f, 0, GX2_CLEAR_FLAGS_DEPTH | GX2_CLEAR_FLAGS_STENCIL); + } +} + +static int drc_ticks; +void Gfx_EndFrame(void) { + GX2ColorBuffer* buf; + + buf = WHBGfxGetTVColourBuffer(); + GX2CopyColorBufferToScanBuffer(buf, GX2_SCAN_TARGET_TV); + + GX2ContextState* state = WHBGfxGetDRCContextState(); + GX2SetContextState(state); + drc_ticks = (drc_ticks + 1) % 200; + buf = WHBGfxGetDRCColourBuffer(); + GX2ClearColor(buf, drc_ticks / 200.0f, drc_ticks / 200.0f, drc_ticks / 200.0f, 1.0f); + GX2CopyColorBufferToScanBuffer(buf, GX2_SCAN_TARGET_DRC); + + GX2SwapScanBuffers(); + GX2Flush(); + GX2DrawDone(); + GX2SetTVEnable(TRUE); + GX2SetDRCEnable(TRUE); + + if (gfx_minFrameMs) LimitFPS(); +} + +cc_bool Gfx_WarnIfNecessary(void) { return false; } + +void Gfx_GetApiInfo(cc_string* info) { + String_AppendConst(info, "-- Using Wii U --\n"); + PrintMaxTextureInfo(info); +} + +void Gfx_OnWindowResize(void) { + +} + +void Gfx_SetViewport(int x, int y, int w, int h) { + GX2SetViewport(x, y, w, h, 0.0f, 1.0f); + GX2SetScissor( x, y, w, h); +} + +void Gfx_3DS_SetRenderScreen1(enum Screen3DS screen) { + GX2ContextState* tv_state = WHBGfxGetTVContextState(); + GX2ContextState* drc_state = WHBGfxGetDRCContextState(); // TODO + + GX2SetContextState(screen == TOP_SCREEN ? tv_state : drc_state); +} +#endif diff --git a/src/Graphics_Xbox.c b/src/Graphics_Xbox.c index 5dcbf96a9..e4c0d9378 100644 --- a/src/Graphics_Xbox.c +++ b/src/Graphics_Xbox.c @@ -106,6 +106,7 @@ static void ResetState(void) { } static GfxResourceID white_square; + void Gfx_Create(void) { Gfx.MaxTexWidth = 512; Gfx.MaxTexHeight = 512; // TODO: 1024? @@ -141,8 +142,7 @@ void Gfx_FreeState(void) { } *#########################################################################################################################*/ typedef struct CCTexture_ { cc_uint32 width, height; - cc_uint32 pitch, pad; - cc_uint32 pixels[]; + cc_uint32* pixels; } CCTexture; // See Graphics_Dreamcast.c for twiddling explanation @@ -174,7 +174,7 @@ static unsigned Interleave(unsigned x) { hi_X = (x & shifted_mask) << shift_bits; \ X = lo_X | hi_X; -static void ConvertTexture(cc_uint32* dst, struct Bitmap* bmp) { +static void ConvertTexture(cc_uint32* dst, struct Bitmap* bmp, int rowWidth) { unsigned min_dimension; unsigned interleave_mask, interleaved_bits; unsigned shifted_mask, shift_bits; @@ -182,10 +182,11 @@ static void ConvertTexture(cc_uint32* dst, struct Bitmap* bmp) { unsigned lo_X, hi_X, X; Twiddle_CalcFactors(bmp->width, bmp->height); - cc_uint32* src = bmp->scan0; for (int y = 0; y < bmp->height; y++) { Twiddle_CalcY(y); + cc_uint32* src = bmp->scan0 + y * rowWidth; + for (int x = 0; x < bmp->width; x++, src++) { Twiddle_CalcX(x); @@ -194,14 +195,14 @@ static void ConvertTexture(cc_uint32* dst, struct Bitmap* bmp) { } } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { - int size = 16 + bmp->width * bmp->height * 4; - CCTexture* tex = MmAllocateContiguousMemoryEx(size, 0, MAX_RAM_ADDR, 16, PAGE_WRITECOMBINE | PAGE_READWRITE); +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { + int size = bmp->width * bmp->height * 4; + CCTexture* tex = Mem_Alloc(1, sizeof(CCTexture), "GPU texture"); + tex->pixels = MmAllocateContiguousMemoryEx(size, 0, MAX_RAM_ADDR, 0, PAGE_WRITECOMBINE | PAGE_READWRITE); tex->width = bmp->width; tex->height = bmp->height; - tex->pitch = bmp->width * 4; - ConvertTexture(tex->pixels, bmp); + ConvertTexture(tex->pixels, bmp, rowWidth); return tex; } @@ -232,12 +233,13 @@ void Gfx_UpdateTexture(GfxResourceID texId, int originX, int originY, struct Bit } } -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); -} - void Gfx_DeleteTexture(GfxResourceID* texId) { - // TODO + CCTexture* tex = (CCTexture*)(*texId); + if (!tex) return; + + MmFreeContiguousMemory(tex->pixels); + Mem_Free(tex); + *texId = NULL; } void Gfx_EnableMipmaps(void) { } @@ -255,15 +257,18 @@ void Gfx_BindTexture(GfxResourceID texId) { // set texture stage 0 state p = pb_push1(p, NV097_SET_TEXTURE_OFFSET, (DWORD)tex->pixels & 0x03ffffff); p = pb_push1(p, NV097_SET_TEXTURE_FORMAT, - 0xA | - MASK(NV097_SET_TEXTURE_FORMAT_COLOR, NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8) | + MASK(NV097_SET_TEXTURE_FORMAT_CONTEXT_DMA, 2) | + MASK(NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE, NV097_SET_TEXTURE_FORMAT_BORDER_SOURCE_COLOR) | + MASK(NV097_SET_TEXTURE_FORMAT_COLOR, NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8) | MASK(NV097_SET_TEXTURE_FORMAT_DIMENSIONALITY, 2) | // textures have U and V MASK(NV097_SET_TEXTURE_FORMAT_MIPMAP_LEVELS, 1) | MASK(NV097_SET_TEXTURE_FORMAT_BASE_SIZE_U, log_u) | MASK(NV097_SET_TEXTURE_FORMAT_BASE_SIZE_V, log_v) | - MASK(NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P, 1)); // 1 slice + MASK(NV097_SET_TEXTURE_FORMAT_BASE_SIZE_P, 0)); // log2(1) slice = 0 p = pb_push1(p, NV097_SET_TEXTURE_CONTROL0, - NV097_SET_TEXTURE_CONTROL0_ENABLE | NV097_SET_TEXTURE_CONTROL0_MAX_LOD_CLAMP); + NV097_SET_TEXTURE_CONTROL0_ENABLE | + MASK(NV097_SET_TEXTURE_CONTROL0_MIN_LOD_CLAMP, 0) | + MASK(NV097_SET_TEXTURE_CONTROL0_MAX_LOD_CLAMP, 1)); p = pb_push1(p, NV097_SET_TEXTURE_ADDRESS, 0x00010101); // modes (0x0W0V0U wrapping: 1=wrap 2=mirror 3=clamp 4=border 5=clamp to edge) p = pb_push1(p, NV097_SET_TEXTURE_FILTER, @@ -282,7 +287,7 @@ void Gfx_BindTexture(GfxResourceID texId) { *#########################################################################################################################*/ static PackedCol clearColor; -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { clearColor = color; } @@ -319,12 +324,7 @@ void Gfx_SetDepthTest(cc_bool enabled) { } -void Gfx_DepthOnlyRendering(cc_bool depthOnly) { - cc_bool enabled = !depthOnly; - Gfx_SetColWriteMask(enabled, enabled, enabled, enabled); -} - -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { unsigned mask = 0; if (r) mask |= NV097_SET_COLOR_MASK_RED_WRITE_ENABLE; if (g) mask |= NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE; @@ -336,6 +336,12 @@ void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { pb_end(p); } +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); +} + /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* @@ -360,15 +366,17 @@ void Gfx_BeginFrame(void) { pb_target_back_buffer(); } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { int width = pb_back_buffer_width(); int height = pb_back_buffer_height(); // TODO do ourselves - pb_erase_depth_stencil_buffer(0, 0, width, height); - pb_fill(0, 0, width, height, clearColor); - //pb_erase_text_screen(); + if (buffers & GFX_BUFFER_DEPTH) + pb_erase_depth_stencil_buffer(0, 0, width, height); + if (buffers & GFX_BUFFER_COLOR) + pb_fill(0, 0, width, height, clearColor); + //pb_erase_text_screen(); while (pb_busy()) { } // Wait for completion TODO: necessary?? } @@ -500,12 +508,12 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float } // https://github.com/XboxDev/nxdk/blob/master/samples/mesh/math3d.c#L292 -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { float zNear = 0.1f; /* Source https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixperspectivefovrh */ /* NOTE: This calculation is shared with Direct3D 11 backend */ - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); *matrix = Matrix_Identity; matrix->row1.x = c / aspect; @@ -528,15 +536,11 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f void Gfx_OnWindowResize(void) { } +static struct Vec4 vp_scale = { 320, -240, 8388608, 1 }; +static struct Vec4 vp_offset = { 320, 240, 8388608, 1 }; static struct Matrix _view, _proj, _mvp; -void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { - struct Matrix* dst = type == MATRIX_PROJECTION ? &_proj : &_view; - *dst = *matrix; - - struct Matrix combined; - Matrix_Mul(&combined, &_view, &_proj); - +static void UpdateVSConstants(void) { uint32_t* p; p = pb_begin(); @@ -547,11 +551,11 @@ void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { p = pb_push1(p, NV097_SET_TRANSFORM_CONSTANT_LOAD, 96); // upload transformation matrix - pb_push(p++, NV097_SET_TRANSFORM_CONSTANT, 4*4 + 4); - Mem_Copy(p, &combined, 16 * 4); p += 16; + pb_push(p++, NV097_SET_TRANSFORM_CONSTANT, 4*4 + 4 + 4); + Mem_Copy(p, &_mvp, 16 * 4); p += 16; // Upload viewport too - struct Vec4 viewport = { 320, 240, 8388608, 1 }; - Mem_Copy(p, &viewport, 4 * 4); p += 4; + Mem_Copy(p, &vp_scale, 4 * 4); p += 4; + Mem_Copy(p, &vp_offset, 4 * 4); p += 4; // Upload constants too //struct Vec4 v = { 1, 1, 1, 1 }; //Mem_Copy(p, &v, 4 * 4); p += 4; @@ -560,6 +564,14 @@ void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { pb_end(p); } +void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { + struct Matrix* dst = type == MATRIX_PROJECTION ? &_proj : &_view; + *dst = *matrix; + + Matrix_Mul(&_mvp, &_view, &_proj); + UpdateVSConstants(); +} + void Gfx_LoadIdentityMatrix(MatrixType type) { Gfx_LoadMatrix(type, &Matrix_Identity); } @@ -570,6 +582,20 @@ void Gfx_EnableTextureOffset(float x, float y) { void Gfx_DisableTextureOffset(void) { } +void Gfx_SetViewport(int x, int y, int w, int h) { + vp_scale.x = w * 0.5f; + vp_scale.y = h * -0.5f; + vp_offset.x = x + w * 0.5f; + vp_offset.y = y + h * 0.5f; + + uint32_t* p; + p = pb_begin(); + // NV097_SET_SURFACE_CLIP_HORIZONTAL followed by NV097_SET_SURFACE_CLIP_VERTICAL + p = pb_push2(p, NV097_SET_SURFACE_CLIP_HORIZONTAL, x | (w << 16), y | (h << 16)); + pb_end(p); +} + + /*########################################################################################################################* diff --git a/src/Graphics_Xbox360.c b/src/Graphics_Xbox360.c index 9778c8cc2..745729c02 100644 --- a/src/Graphics_Xbox360.c +++ b/src/Graphics_Xbox360.c @@ -4,20 +4,67 @@ #include "Errors.h" #include "Window.h" #include +#include + +#include "../misc/xbox360/ps_coloured.h" +#include "../misc/xbox360/vs_coloured.h" +#include "../misc/xbox360/ps_textured.h" +#include "../misc/xbox360/vs_textured.h" +static struct XenosShader* shdr_tex_vs; +static struct XenosShader* shdr_tex_ps; +static struct XenosShader* shdr_col_vs; +static struct XenosShader* shdr_col_ps; static struct XenosDevice device; static struct XenosDevice* xe; -void Gfx_Create(void) { +static const struct XenosVBFFormat textured_vbf = { +3, { + { XE_USAGE_POSITION, 0, XE_TYPE_FLOAT4 }, + { XE_USAGE_COLOR, 0, XE_TYPE_UBYTE4 }, + { XE_USAGE_TEXCOORD, 0, XE_TYPE_FLOAT2 } +} }; + +static const struct XenosVBFFormat coloured_vbf = { +2, { + { XE_USAGE_POSITION, 0, XE_TYPE_FLOAT4 }, + { XE_USAGE_COLOR, 0, XE_TYPE_UBYTE4 } +} }; + +static void CreateState(void) { xe = &device; Xe_Init(xe); + edram_init(xe); - struct XenosSurface *fb = Xe_GetFramebufferSurface(xe); + struct XenosSurface* fb = Xe_GetFramebufferSurface(xe); Xe_SetRenderTarget(xe, fb); +} - Gfx.Created = true; - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; +static void CreateShaders(void) { + shdr_tex_vs = Xe_LoadShaderFromMemory(xe, (void*)vs_textured); + Xe_InstantiateShader(xe, shdr_tex_vs, 0); + Xe_ShaderApplyVFetchPatches(xe, shdr_tex_vs, 0, &textured_vbf); + shdr_tex_ps = Xe_LoadShaderFromMemory(xe, (void*)ps_textured); + Xe_InstantiateShader(xe, shdr_tex_ps, 0); + + shdr_col_vs = Xe_LoadShaderFromMemory(xe, (void*)vs_coloured); + Xe_InstantiateShader(xe, shdr_col_vs, 0); + Xe_ShaderApplyVFetchPatches(xe, shdr_col_vs, 0, &coloured_vbf); + shdr_col_ps = Xe_LoadShaderFromMemory(xe, (void*)ps_coloured); + Xe_InstantiateShader(xe, shdr_col_ps, 0); +} + +void Gfx_Create(void) { + if (!Gfx.Created) { + CreateState(); + CreateShaders(); + } + Gfx.Created = true; + Gfx_SetVertexFormat(VERTEX_FORMAT_COLOURED); + + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; } cc_bool Gfx_TryRestoreContext(void) { @@ -33,14 +80,12 @@ static void Gfx_FreeState(void) { } static void Gfx_RestoreState(void) { - Gfx_SetFaceCulling(false); InitDefaultResources(); - gfx_format = -1; + Gfx_SetFaceCulling(false); + Gfx_SetAlphaBlending(false); Xe_SetAlphaFunc(xe, XE_CMP_GREATER); Xe_SetAlphaRef(xe, 0.5f); - Xe_SetDestBlend(xe, XE_BLEND_SRCALPHA); - Xe_SetSrcBlend(xe, XE_BLEND_INVSRCALPHA); Xe_SetZFunc(xe, XE_CMP_LESSEQUAL); } @@ -48,16 +93,7 @@ static void Gfx_RestoreState(void) { /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ -static void SetTextureData(struct XenosSurface* xtex, struct Bitmap* bmp) { - void* dst = Xe_Surface_LockRect(xe, xtex, 0, 0, bmp->width, bmp->height, XE_LOCK_WRITE); - cc_uint32 size = Bitmap_DataSize(bmp->width, bmp->height); - - Mem_Copy(dst, bmp->scan0, size); - - Xe_Surface_Unlock(xe, xtex); -} - -static void SetTexturePartData(struct XenosSurface* xtex, int x, int y, const struct Bitmap* bmp, int rowWidth, int lvl) { +static void SetTextureData(struct XenosSurface* xtex, int x, int y, const struct Bitmap* bmp, int rowWidth, int lvl) { void* dst = Xe_Surface_LockRect(xe, xtex, x, y, bmp->width, bmp->height, XE_LOCK_WRITE); CopyTextureData(dst, bmp->width * 4, bmp, rowWidth << 2); @@ -65,19 +101,15 @@ static void SetTexturePartData(struct XenosSurface* xtex, int x, int y, const st Xe_Surface_Unlock(xe, xtex); } -static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { +static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) { struct XenosSurface* xtex = Xe_CreateTexture(xe, bmp->width, bmp->height, 1, XE_FMT_8888, 0); - SetTextureData(xtex, bmp); + SetTextureData(xtex, 0, 0, bmp, rowWidth, 0); return xtex; } void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, int rowWidth, cc_bool mipmaps) { struct XenosSurface* xtex = (struct XenosSurface*)texId; - SetTexturePartData(xtex, x, y, part, rowWidth, 0); -} - -void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); + SetTextureData(xtex, x, y, part, rowWidth, 0); } void Gfx_BindTexture(GfxResourceID texId) { @@ -91,8 +123,6 @@ void Gfx_DeleteTexture(GfxResourceID* texId) { *texId = NULL; } -void Gfx_SetTexturing(cc_bool enabled) { } - void Gfx_EnableMipmaps(void) { } // TODO void Gfx_DisableMipmaps(void) { } // TODO @@ -101,8 +131,6 @@ void Gfx_DisableMipmaps(void) { } // TODO /*########################################################################################################################* *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ -static cc_bool gfx_alphaTesting, gfx_alphaBlending; - void Gfx_SetFaceCulling(cc_bool enabled) { Xe_SetCullMode(xe, enabled ? XE_CULL_CW : XE_CULL_NONE); } @@ -128,32 +156,29 @@ void Gfx_SetFogMode(FogFunc func) { } void Gfx_SetAlphaTest(cc_bool enabled) { - if (gfx_alphaTesting == enabled) return; - gfx_alphaTesting = enabled; Xe_SetAlphaTestEnable(xe, enabled); } void Gfx_SetAlphaBlending(cc_bool enabled) { - if (gfx_alphaBlending == enabled) return; - gfx_alphaBlending = enabled; - - if (Gfx.LostContext) return; - //IDirect3DDevice9_SetRenderState(device, D3DRS_ALPHABLENDENABLE, enabled); - // TODO + if (enabled) { + Xe_SetBlendControl(xe, + XE_BLEND_SRCALPHA, XE_BLENDOP_ADD, XE_BLEND_INVSRCALPHA, + XE_BLEND_SRCALPHA, XE_BLENDOP_ADD, XE_BLEND_INVSRCALPHA); + } else { + Xe_SetBlendControl(xe, + XE_BLEND_ONE, XE_BLENDOP_ADD, XE_BLEND_ZERO, + XE_BLEND_ONE, XE_BLENDOP_ADD, XE_BLEND_ZERO); + } } void Gfx_SetAlphaArgBlend(cc_bool enabled) { // TODO } -void Gfx_ClearCol(PackedCol color) { +void Gfx_ClearColor(PackedCol color) { Xe_SetClearColor(xe, color); } -void Gfx_SetColWriteMask(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { - // TODO -} - void Gfx_SetDepthTest(cc_bool enabled) { Xe_SetZEnable(xe, enabled); } @@ -162,10 +187,16 @@ void Gfx_SetDepthWrite(cc_bool enabled) { Xe_SetZWrite(xe, enabled); } -void Gfx_DepthOnlyRendering(cc_bool depthOnly) { +static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { // TODO } +void Gfx_DepthOnlyRendering(cc_bool depthOnly) { + cc_bool enabled = !depthOnly; + SetColorWrite(enabled & gfx_colorMask[0], enabled & gfx_colorMask[1], + enabled & gfx_colorMask[2], enabled & gfx_colorMask[3]); +} + /*########################################################################################################################* *-------------------------------------------------------Index buffers-----------------------------------------------------* @@ -177,6 +208,7 @@ GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { void* dst = Xe_IB_Lock(xe, xib, 0, size, XE_LOCK_WRITE); fillFunc((cc_uint16*)dst, count, obj); Xe_IB_Unlock(xe, xib); + return xib; } void Gfx_BindIb(GfxResourceID ib) { @@ -237,7 +269,7 @@ void* Gfx_LockDynamicVb(GfxResourceID vb, VertexFormat fmt, int count) { return Gfx_LockVb(vb, fmt, count); } -void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_UnlockVb(vb); } +void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_UnlockVb(vb); Gfx_BindVb(vb); } /*########################################################################################################################* @@ -246,26 +278,24 @@ void Gfx_UnlockDynamicVb(GfxResourceID vb) { Gfx_UnlockVb(vb); } void Gfx_SetVertexFormat(VertexFormat fmt) { if (fmt == gfx_format) return; gfx_format = fmt; - + gfx_stride = strideSizes[fmt]; + if (fmt == VERTEX_FORMAT_COLOURED) { - /* it's necessary to unbind the texture, otherwise the alpha from the last bound texture */ - /* gets used - because D3DTSS_ALPHAOP texture stage state is still set to D3DTOP_SELECTARG1 */ Xe_SetTexture(xe, 0, NULL); - /* IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_COLOROP, fmt == VERTEX_FORMAT_COLOURED ? D3DTOP_DISABLE : D3DTOP_MODULATE); */ - /* IDirect3DDevice9_SetTextureStageState(device, 0, D3DTSS_ALPHAOP, fmt == VERTEX_FORMAT_COLOURED ? D3DTOP_DISABLE : D3DTOP_SELECTARG1); */ - /* SetTexture(NULL) seems to be enough, not really required to call SetTextureStageState */ + Xe_SetShader(xe, SHADER_TYPE_PIXEL, shdr_col_ps, 0); + Xe_SetShader(xe, SHADER_TYPE_VERTEX, shdr_col_vs, 0); + } else { + Xe_SetShader(xe, SHADER_TYPE_PIXEL, shdr_tex_ps, 0); + Xe_SetShader(xe, SHADER_TYPE_VERTEX, shdr_tex_vs, 0); } - - // TODO } void Gfx_DrawVb_Lines(int verticesCount) { - /* NOTE: Skip checking return result for Gfx_DrawXYZ for performance */ Xe_DrawPrimitive(xe, XE_PRIMTYPE_LINELIST, 0, verticesCount >> 1); } void Gfx_DrawVb_IndexedTris(int verticesCount) { - Xe_DrawIndexedPrimitive(xe, XE_PRIMTYPE_TRIANGLELIST, + Xe_DrawIndexedPrimitive(xe, XE_PRIMTYPE_TRIANGLELIST, // TODO QUADLIST instead? 0, 0, verticesCount, 0, verticesCount >> 1); } @@ -283,12 +313,19 @@ void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { /*########################################################################################################################* *---------------------------------------------------------Matrices--------------------------------------------------------* *#########################################################################################################################*/ +static struct Matrix _view, _proj, _mvp; + void Gfx_LoadMatrix(MatrixType type, const struct Matrix* matrix) { - // TODO + struct Matrix* dst = type == MATRIX_PROJECTION ? &_proj : &_view; + *dst = *matrix; + + Matrix_Mul(&_mvp, &_view, &_proj); + // TODO: Is this a global uniform, or does it need to be reloaded on shader change? + Xe_SetVertexShaderConstantF(xe, 0, (float*)&_mvp, 4); } -void Gfx_LoadIdentityMatrix(MatrixType type) { - // TODO +void Gfx_LoadIdentityMatrix(MatrixType type) { + Gfx_LoadMatrix(type, &Matrix_Identity); } void Gfx_EnableTextureOffset(float x, float y) { @@ -303,28 +340,28 @@ void Gfx_CalcOrthoMatrix(struct Matrix* matrix, float width, float height, float // TODO verify this *matrix = Matrix_Identity; - matrix->row1.X = 2.0f / width; - matrix->row2.Y = -2.0f / height; - matrix->row3.Z = 1.0f / (zNear - zFar); + matrix->row1.x = 2.0f / width; + matrix->row2.y = -2.0f / height; + matrix->row3.z = 1.0f / (zNear - zFar); - matrix->row4.X = -1.0f; - matrix->row4.Y = 1.0f; - matrix->row4.Z = zNear / (zNear - zFar); + matrix->row4.x = -1.0f; + matrix->row4.y = 1.0f; + matrix->row4.z = zNear / (zNear - zFar); } -static double Cotangent(double x) { return Math_Cos(x) / Math_Sin(x); } +static float Cotangent(float x) { return Math_CosF(x) / Math_SinF(x); } void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, float zFar) { // TODO verify this float zNear = 0.1f; - float c = (float)Cotangent(0.5f * fov); + float c = Cotangent(0.5f * fov); *matrix = Matrix_Identity; - matrix->row1.X = c / aspect; - matrix->row2.Y = c; - matrix->row3.Z = zFar / (zNear - zFar); - matrix->row3.W = -1.0f; - matrix->row4.Z = (zNear * zFar) / (zNear - zFar); - matrix->row4.W = 0.0f; + matrix->row1.x = c / aspect; + matrix->row2.y = c; + matrix->row3.z = zFar / (zNear - zFar); + matrix->row3.w = -1.0f; + matrix->row4.z = (zNear * zFar) / (zNear - zFar); + matrix->row4.w = 0.0f; } @@ -343,7 +380,8 @@ void Gfx_SetFpsLimit(cc_bool vsync, float minFrameMs) { void Gfx_BeginFrame(void) { } -void Gfx_Clear(void) { +void Gfx_ClearBuffers(GfxBuffers buffers) { + // TODO clear only some buffers // Xe_Clear is just a Resolve anyways } @@ -364,4 +402,6 @@ void Gfx_GetApiInfo(cc_string* info) { void Gfx_OnWindowResize(void) { } + +void Gfx_SetViewport(int x, int y, int w, int h) { } #endif diff --git a/src/Gui.c b/src/Gui.c index d6287e69a..06ebef00a 100644 --- a/src/Gui.c +++ b/src/Gui.c @@ -35,7 +35,7 @@ static CC_NOINLINE int GetWindowScale(void) { /* Use larger UI scaling on mobile */ /* TODO move this DPI scaling elsewhere.,. */ #ifndef CC_BUILD_DUALSCREEN - if (!Input_TouchMode) { + if (!Gui.TouchUI) { #endif widthScale /= DisplayInfo.ScaleX; heightScale /= DisplayInfo.ScaleY; @@ -58,9 +58,15 @@ float Gui_GetInventoryScale(void) { } float Gui_GetChatScale(void) { - return Gui_Scale(GetWindowScale() * Gui.RawChatScale); + if (Gui.AutoScaleChat) return Gui_Scale(GetWindowScale() * Gui.RawChatScale); + return Gui.RawChatScale; } +float Gui_GetCrosshairScale(void) { + return Gui_Scale((Window_Main.Height / 480.0f)) * Gui.RawCrosshairScale; +} + + void Gui_MakeTitleFont(struct FontDesc* font) { Font_Make(font, 16, FONT_FLAGS_BOLD); } void Gui_MakeBodyFont(struct FontDesc* font) { Font_Make(font, 16, FONT_FLAGS_NONE); } @@ -96,6 +102,12 @@ void Gui_ShowDefault(void) { #endif } +#ifdef CC_BUILD_TOUCH +void Gui_SetTouchUI(cc_bool enabled) { + Gui.TouchUI = enabled; /* TODO toggle or not */ +} +#endif + static void LoadOptions(void) { Gui.DefaultLines = Game_ClassicMode ? 10 : 12; Gui.Chatlines = Options_GetInt(OPT_CHATLINES, 0, GUI_MAX_CHATLINES, Gui.DefaultLines); @@ -112,7 +124,10 @@ static void LoadOptions(void) { Gui.RawInventoryScale = Options_GetFloat(OPT_INVENTORY_SCALE, 0.25f, 5.0f, 1.0f); Gui.RawHotbarScale = Options_GetFloat(OPT_HOTBAR_SCALE, 0.25f, 5.0f, 1.0f); Gui.RawChatScale = Options_GetFloat(OPT_CHAT_SCALE, 0.25f, 5.0f, 1.0f); + Gui.RawCrosshairScale = Options_GetFloat(OPT_CROSSHAIR_SCALE, 0.25f, 5.0f, 1.0f); Gui.RawTouchScale = Options_GetFloat(OPT_TOUCH_SCALE, 0.25f, 5.0f, 1.0f); + + Gui.AutoScaleChat = Options_GetBool(OPT_CHAT_AUTO_SCALE, true); } static void LoseAllScreens(void) { @@ -233,13 +248,12 @@ void Gui_Remove(struct Screen* s) { } void Gui_Add(struct Screen* s, int priority) { + struct Screen* existing; int i; Gui_RemoveCore(s); - /* Backwards loop since removing changes count and gui_screens */ - for (i = Gui.ScreensCount - 1; i >= 0; i--) - { - if (priorities[i] == priority) Gui_RemoveCore(Gui_Screens[i]); - } + + existing = Gui_GetScreen(priority); + if (existing) Gui_RemoveCore(existing); Gui_AddCore(s, priority); Gui_OnScreensChanged(); @@ -272,6 +286,15 @@ struct Screen* Gui_GetClosable(void) { return NULL; } +struct Screen* Gui_GetScreen(int priority) { + int i; + for (i = 0; i < Gui.ScreensCount; i++) + { + if (priorities[i] == priority) return Gui_Screens[i]; + } + return NULL; +} + void Gui_UpdateInputGrab(void) { Gui.InputGrab = Gui_GetInputGrab(); Camera_CheckFocus(); @@ -285,7 +308,7 @@ void Gui_ShowPauseMenu(void) { } } -void Gui_RenderGui(double delta) { +void Gui_RenderGui(float delta) { struct Screen* s; int i; @@ -349,8 +372,8 @@ void TextAtlas_Make(struct TextAtlas* atlas, const cc_string* chars, struct Font Context2D_Free(&ctx); atlas->uScale = 1.0f / (float)ctx.bmp.width; - atlas->tex.uv.U2 = atlas->offset * atlas->uScale; - atlas->tex.Width = atlas->offset; + atlas->tex.uv.u2 = atlas->offset * atlas->uScale; + atlas->tex.width = atlas->offset; } void TextAtlas_Free(struct TextAtlas* atlas) { Gfx_DeleteTexture(&atlas->tex.ID); } @@ -359,9 +382,9 @@ void TextAtlas_Add(struct TextAtlas* atlas, int charI, struct VertexTextured** v struct Texture part = atlas->tex; int width = atlas->widths[charI]; - part.x = atlas->curX; part.Width = width; - part.uv.U1 = atlas->offsets[charI] * atlas->uScale; - part.uv.U2 = part.uv.U1 + width * atlas->uScale; + part.x = atlas->curX; part.width = width; + part.uv.u1 = atlas->offsets[charI] * atlas->uScale; + part.uv.u2 = part.uv.u1 + width * atlas->uScale; atlas->curX += width; Gfx_Make2DQuad(&part, PACKEDCOL_WHITE, vertices); @@ -391,7 +414,7 @@ void Widget_SetLocation(void* widget, cc_uint8 horAnchor, cc_uint8 verAnchor, in w->horAnchor = horAnchor; w->verAnchor = verAnchor; w->xOffset = Display_ScaleX(xOffset); w->yOffset = Display_ScaleY(yOffset); - Widget_Layout(w); + if (w->VTABLE) Widget_Layout(w); } void Widget_CalcPosition(void* widget) { @@ -420,6 +443,7 @@ void Widget_Reset(void* widget) { w->verAnchor = ANCHOR_MIN; w->xOffset = 0; w->yOffset = 0; w->MenuClick = NULL; + w->meta.ptr = NULL; } int Widget_Contains(void* widget, int x, int y) { @@ -441,7 +465,7 @@ void Widget_SetDisabled(void* widget, int disabled) { /*########################################################################################################################* *-------------------------------------------------------Screen base-------------------------------------------------------* *#########################################################################################################################*/ -void Screen_Render2Widgets(void* screen, double delta) { +void Screen_Render2Widgets(void* screen, float delta) { struct Screen* s = (struct Screen*)screen; struct Widget** widgets = s->widgets; int i, offset = 0; @@ -490,19 +514,6 @@ int Screen_DoPointerDown(void* screen, int id, int x, int y) { return i; } -int Screen_Index(void* screen, void* widget) { - struct Screen* s = (struct Screen*)screen; - struct Widget** widgets = s->widgets; - int i; - - struct Widget* w = (struct Widget*)widget; - for (i = 0; i < s->numWidgets; i++) - { - if (widgets[i] == w) return i; - } - return -1; -} - int Screen_CalcDefaultMaxVertices(void* screen) { struct Screen* s = (struct Screen*)screen; struct Widget** widgets = s->widgets; @@ -564,6 +575,44 @@ int Screen_InputDown(void* screen, int key) { return key < CCKEY_F1 || key > CC void Screen_InputUp(void* screen, int key) { } void Screen_PointerUp(void* s, int id, int x, int y) { } +/*########################################################################################################################* +*------------------------------------------------------Input handling-----------------------------------------------------* +*#########################################################################################################################*/ +static void OnMouseWheel(void* obj, float delta) { + struct Screen* s; + int i; + + for (i = 0; i < Gui.ScreensCount; i++) { + s = Gui_Screens[i]; + s->dirty = true; + if (s->VTABLE->HandlesMouseScroll(s, delta)) return; + } +} + +static void OnPointerMove(void* obj, int idx) { + struct Screen* s; + int i, x = Pointers[idx].x, y = Pointers[idx].y; + + for (i = 0; i < Gui.ScreensCount; i++) { + s = Gui_Screens[i]; + s->dirty = true; + if (s->VTABLE->HandlesPointerMove(s, 1 << idx, x, y)) return; + } +} + +static void OnAxisUpdate(void* obj, int port, int axis, float x, float y) { + struct Screen* s; + int i; + + for (i = 0; i < Gui.ScreensCount; i++) { + s = Gui_Screens[i]; + if (!s->VTABLE->HandlesPadAxis) continue; + + s->dirty = true; + if (s->VTABLE->HandlesPadAxis(s, axis, x, y)) return; + } +} + /*########################################################################################################################* *------------------------------------------------------Gui component------------------------------------------------------* @@ -634,17 +683,22 @@ static void OnInit(void) { TextureEntry_Register(&icons_entry); TextureEntry_Register(&touch_entry); + Event_Register_(&InputEvents.Wheel, NULL, OnMouseWheel); + Event_Register_(&PointerEvents.Moved, NULL, OnPointerMove); + Event_Register_(&ControllerEvents.AxisUpdate, NULL, OnAxisUpdate); + #ifdef CC_BUILD_DUALSCREEN struct Context2D ctx; Context2D_Alloc(&ctx, 32, 32); Gradient_Noise(&ctx, BitmapColor_RGB(0x40, 0x30, 0x20), 6, 0, 0, ctx.width, ctx.height); Context2D_MakeTexture(&touchBgTex, &ctx); Context2D_Free(&ctx); + // Tile the texture to fill the entire screen - int tilesX = (320 + ctx.width - 1) / ctx.width; - int tilesY = (240 + ctx.height - 1) / ctx.height; - touchBgTex.Width *= tilesX; touchBgTex.Height *= tilesY; - touchBgTex.uv.U2 *= tilesX; touchBgTex.uv.V2 *= tilesY; + int tilesX = Math_CeilDiv(Window_Alt.Width, ctx.width); + int tilesY = Math_CeilDiv(Window_Alt.Height, ctx.height); + touchBgTex.width *= tilesX; touchBgTex.height *= tilesY; + touchBgTex.uv.u2 *= tilesX; touchBgTex.uv.v2 *= tilesY; #endif Event_Register_(&ChatEvents.FontChanged, NULL, OnFontChanged); @@ -654,13 +708,6 @@ static void OnInit(void) { Event_Register_(&WindowEvents.Resized, NULL, OnResize); Event_Register_(&InputEvents.TextChanged, NULL, OnTextChanged); -#ifdef CC_BUILD_TOUCH - #define DEFAULT_SP_ONSCREEN (ONSCREEN_BTN_FLY | ONSCREEN_BTN_SPEED) - #define DEFAULT_MP_ONSCREEN (ONSCREEN_BTN_FLY | ONSCREEN_BTN_SPEED | ONSCREEN_BTN_CHAT) - Gui._onscreenButtons = Options_GetInt(OPT_TOUCH_BUTTONS, 0, Int32_MaxValue, - Server.IsSinglePlayer ? DEFAULT_SP_ONSCREEN : DEFAULT_MP_ONSCREEN); -#endif - LoadOptions(); Gui_ShowDefault(); } @@ -682,4 +729,4 @@ struct IGameComponent Gui_Component = { OnReset, /* Reset */ NULL, /* OnNewMap */ NULL, /* OnNewMapLoaded */ -}; \ No newline at end of file +}; diff --git a/src/Gui.h b/src/Gui.h index 089c80834..cf2fcb330 100644 --- a/src/Gui.h +++ b/src/Gui.h @@ -42,20 +42,24 @@ CC_VAR extern struct _GuiData { cc_bool ShowFPS; /* Whether classic-style inventory is used */ cc_bool ClassicInventory; - float RawHotbarScale, RawChatScale, RawInventoryScale; + float RawHotbarScale, RawChatScale, RawInventoryScale, RawCrosshairScale; GfxResourceID GuiTex, GuiClassicTex, IconsTex, TouchTex; int DefaultLines; - /* (internal) Bitmask of on-screen buttons, see Input.h */ - int _onscreenButtons; + int _unused; float RawTouchScale; /* The highest priority screen that has grabbed input. */ struct Screen* InputGrab; + /* Whether chat automatically scales based on window size. */ + cc_bool AutoScaleChat; + /* Whether the touch UI is currently being displayed */ + cc_bool TouchUI; } Gui; float Gui_Scale(float value); float Gui_GetHotbarScale(void); float Gui_GetInventoryScale(void); float Gui_GetChatScale(void); +float Gui_GetCrosshairScale(void); CC_NOINLINE void Gui_MakeTitleFont(struct FontDesc* font); CC_NOINLINE void Gui_MakeBodyFont(struct FontDesc* font); @@ -65,11 +69,11 @@ struct ScreenVTABLE { /* Initialises persistent state. */ void (*Init)(void* elem); /* Updates this screen, called every frame just before Render(). */ - void (*Update)(void* elem, double delta); + void (*Update)(void* elem, float delta); /* Frees/releases persistent state. */ void (*Free)(void* elem); /* Draws this screen and its widgets on screen. */ - void (*Render)(void* elem, double delta); + void (*Render)(void* elem, float delta); /* Builds the vertex mesh for all the widgets in the screen. */ void (*BuildMesh)(void* elem); /* Returns non-zero if an input press is handled. */ @@ -94,6 +98,8 @@ struct ScreenVTABLE { void (*ContextLost)(void* elem); /* Allocates graphics resources. (textures, vertex buffers, etc) */ void (*ContextRecreated)(void* elem); + /* Returns non-zero if a pad axis update is handled. */ + int (*HandlesPadAxis)(void* elem, int axis, float x, float y); }; #define Screen_Body const struct ScreenVTABLE* VTABLE; \ cc_bool grabsInput; /* Whether this screen grabs input. Causes the cursor to become visible. */ \ @@ -102,16 +108,15 @@ struct ScreenVTABLE { cc_bool dirty; /* Whether this screens needs to have its mesh rebuilt. */ \ int maxVertices; GfxResourceID vb; /* Vertex buffer storing the contents of the screen */ \ struct Widget** widgets; int numWidgets; /* The widgets/individual elements in the screen */ \ - int selectedI; + int selectedI, maxWidgets; /* Represents a container of widgets and other 2D elements. May cover entire window. */ struct Screen { Screen_Body }; /* Calls Widget_Render2 on each widget in the screen. */ -void Screen_Render2Widgets(void* screen, double delta); +void Screen_Render2Widgets(void* screen, float delta); void Screen_UpdateVb(void* screen); struct VertexTextured* Screen_LockVb(void* screen); int Screen_DoPointerDown(void* screen, int id, int x, int y); -int Screen_Index(void* screen, void* w); int Screen_CalcDefaultMaxVertices(void* screen); /* Default mesh building implementation for a screen */ @@ -133,10 +138,13 @@ void Screen_InputUp(void* screen, int key); /* (does nothing) */ void Screen_PointerUp(void* s, int id, int x, int y); + typedef void (*Widget_LeftClick)(void* screen, void* widget); +union WidgetMeta { int val; void* ptr; }; + struct WidgetVTABLE { /* Draws this widget on-screen. */ - void (*Render)(void* elem, double delta); + void (*Render)(void* elem, float delta); /* Destroys allocated graphics resources. */ void (*Free)(void* elem); /* Positions this widget on-screen. */ @@ -159,6 +167,8 @@ struct WidgetVTABLE { int (*Render2)(void* elem, int offset); /* Returns the maximum number of vertices this widget may use */ int (*GetMaxVertices)(void* elem); + /* Returns non-zero if a pad axis update is handled. */ + int (*HandlesPadAxis)(void* elem, int axis, float x, float y); }; #define Widget_Body const struct WidgetVTABLE* VTABLE; \ @@ -167,7 +177,8 @@ struct WidgetVTABLE { cc_uint8 flags; /* Flags controlling the widget's interactability */ \ cc_uint8 horAnchor, verAnchor; /* The reference point for when this widget is resized */ \ int xOffset, yOffset; /* Offset from the reference point */ \ - Widget_LeftClick MenuClick; + Widget_LeftClick MenuClick; \ + union WidgetMeta meta; /* Whether a widget is prevented from being interacted with */ #define WIDGET_FLAG_DISABLED 0x01 @@ -227,6 +238,10 @@ int Gui_Contains(int recX, int recY, int width, int height, int x, int y); int Gui_ContainsPointers(int x, int y, int width, int height); /* Shows HUD and Status screens. */ void Gui_ShowDefault(void); +#ifdef CC_BUILD_TOUCH +/* Sets whether touch UI should be displayed or not */ +void Gui_SetTouchUI(cc_bool enabled); +#endif /* (internal) Removes the screen from the screens list. */ /* NOTE: This does NOT perform the usual 'screens changed' behaviour. */ @@ -243,13 +258,15 @@ CC_API struct Screen* Gui_GetInputGrab(void); struct Screen* Gui_GetBlocksWorld(void); /* Returns highest priority screen that is closable. */ struct Screen* Gui_GetClosable(void); +/* Returns screen with the given priority */ +CC_API struct Screen* Gui_GetScreen(int priority); void Gui_UpdateInputGrab(void); void Gui_ShowPauseMenu(void); void Gui_LayoutAll(void); void Gui_RefreshAll(void); void Gui_Refresh(struct Screen* s); -void Gui_RenderGui(double delta); +void Gui_RenderGui(float delta); #define TEXTATLAS_MAX_WIDTHS 16 struct TextAtlas { @@ -275,7 +292,9 @@ void TextAtlas_AddInt(struct TextAtlas* atlas, int value, struct VertexTextured* #define Elem_OnPointerUp(elem, id, x, y) (elem)->VTABLE->OnPointerUp(elem, id, x, y) #define Elem_HandlesPointerMove(elem, id, x, y) (elem)->VTABLE->HandlesPointerMove(elem, id, x, y) +#define Elem_HandlesPadAxis(elem, axis, x, y) (elem)->VTABLE->HandlesPadAxis(elem, axis, x, y) + #define Widget_BuildMesh(widget, vertices) (widget)->VTABLE->BuildMesh(widget, vertices) #define Widget_Render2(widget, offset) (widget)->VTABLE->Render2(widget, offset) #define Widget_Layout(widget) (widget)->VTABLE->Reposition(widget) -#endif \ No newline at end of file +#endif diff --git a/src/HeldBlockRenderer.c b/src/HeldBlockRenderer.c index 0209c07e5..b0b603ac4 100644 --- a/src/HeldBlockRenderer.c +++ b/src/HeldBlockRenderer.c @@ -17,11 +17,21 @@ static struct Matrix held_blockProj; static cc_bool held_animating, held_breaking, held_swinging; static float held_swingY; -static double held_time, held_period = 0.25; +static float held_time, held_period = 0.25f; static BlockID held_lastBlock; +/* Since not using Entity_SetModel, which normally automatically does this */ +static void SetHeldModel(struct Model* model) { +#ifdef CC_BUILD_CONSOLE + static int maxVertices; + if (model->maxVertices <= maxVertices) return; + + maxVertices = model->maxVertices; + Gfx_DeleteDynamicVb(&held_entity.ModelVB); +#endif +} + static void HeldBlockRenderer_RenderModel(void) { - static const cc_string block = String_FromConst("block"); struct Model* model; Gfx_SetFaceCulling(true); @@ -29,14 +39,16 @@ static void HeldBlockRenderer_RenderModel(void) { /* TODO: Need to properly reallocate per model VB here */ if (Blocks.Draw[held_block] == DRAW_GAS) { - model = LocalPlayer_Instance.Base.Model; + model = Entities.CurPlayer->Base.Model; + SetHeldModel(model); Vec3_Set(held_entity.ModelScale, 1.0f,1.0f,1.0f); Gfx_SetAlphaTest(true); Model_RenderArm(model, &held_entity); Gfx_SetAlphaTest(false); } else { - model = Model_Get(&block); + model = Models.Block; + SetHeldModel(model); Vec3_Set(held_entity.ModelScale, 0.4f,0.4f,0.4f); Gfx_SetupAlphaState(Blocks.Draw[held_block]); @@ -49,7 +61,7 @@ static void HeldBlockRenderer_RenderModel(void) { } static void SetMatrix(void) { - struct Entity* p = &LocalPlayer_Instance.Base; + struct Entity* p = &Entities.CurPlayer->Base; struct Matrix lookAt; Vec3 eye = { 0,0,0 }; eye.y = Entity_GetEyeHeight(p); @@ -59,7 +71,7 @@ static void SetMatrix(void) { static void ResetHeldState(void) { /* Based off details from http://pastebin.com/KFV0HkmD (Thanks goodlyay!) */ - struct Entity* p = &LocalPlayer_Instance.Base; + struct Entity* p = &Entities.CurPlayer->Base; Vec3 eye = { 0,0,0 }; eye.y = Entity_GetEyeHeight(p); held_entity.Position = eye; @@ -106,24 +118,24 @@ static void OnProjectionChanged(void* obj) { https://github.com/UnknownShadow200/ClassicalSharp/wiki/Dig-animation-details */ static void HeldBlockRenderer_DigAnimation(void) { - double sinHalfCircle, sinHalfCircleWeird; + float sinHalfCircle, sinHalfCircleWeird; float t, sqrtLerpPI; t = held_time / held_period; - sinHalfCircle = Math_Sin(t * MATH_PI); + sinHalfCircle = Math_SinF(t * MATH_PI); sqrtLerpPI = Math_SqrtF(t) * MATH_PI; - held_entity.Position.x -= (float)Math_Sin(sqrtLerpPI) * 0.4f; - held_entity.Position.y += (float)Math_Sin(sqrtLerpPI * 2) * 0.2f; - held_entity.Position.z -= (float)sinHalfCircle * 0.2f; + held_entity.Position.x -= Math_SinF(sqrtLerpPI) * 0.4f; + held_entity.Position.y += Math_SinF(sqrtLerpPI * 2) * 0.2f; + held_entity.Position.z -= sinHalfCircle * 0.2f; - sinHalfCircleWeird = Math_Sin(t * t * MATH_PI); - held_entity.RotY -= (float)Math_Sin(sqrtLerpPI) * 80.0f; - held_entity.Yaw -= (float)Math_Sin(sqrtLerpPI) * 80.0f; - held_entity.RotX += (float)sinHalfCircleWeird * 20.0f; + sinHalfCircleWeird = Math_SinF(t * t * MATH_PI); + held_entity.RotY -= Math_SinF(sqrtLerpPI) * 80.0f; + held_entity.Yaw -= Math_SinF(sqrtLerpPI) * 80.0f; + held_entity.RotX += sinHalfCircleWeird * 20.0f; } -static void HeldBlockRenderer_ResetAnim(cc_bool setLastHeld, double period) { +static void HeldBlockRenderer_ResetAnim(cc_bool setLastHeld, float period) { held_time = 0.0f; held_swingY = 0.0f; held_animating = false; held_swinging = false; held_period = period; @@ -135,7 +147,7 @@ static PackedCol HeldBlockRenderer_GetCol(struct Entity* entity) { PackedCol col; float adjPitch, t, scale; - player = &LocalPlayer_Instance.Base; + player = &Entities.CurPlayer->Base; col = player->VTABLE->GetCol(player); /* Adjust pitch so angle when looking straight down is 0. */ @@ -178,13 +190,13 @@ static void OnBlockChanged(void* obj, IVec3 coords, BlockID old, BlockID now) { HeldBlockRenderer_ClickAnim(false); } -static void DoAnimation(double delta, float lastSwingY) { - double t; +static void DoAnimation(float delta, float lastSwingY) { + float t; if (!held_animating) return; if (held_swinging || !held_breaking) { t = held_time / held_period; - held_swingY = -0.4f * (float)Math_Sin(t * MATH_PI); + held_swingY = -0.4f * Math_SinF(t * MATH_PI); held_entity.Position.y += held_swingY; if (held_swinging) { @@ -200,11 +212,11 @@ static void DoAnimation(double delta, float lastSwingY) { held_time += delta; if (held_time > held_period) { - HeldBlockRenderer_ResetAnim(true, 0.25); + HeldBlockRenderer_ResetAnim(true, 0.25f); } } -void HeldBlockRenderer_Render(double delta) { +void HeldBlockRenderer_Render(float delta) { float lastSwingY; struct Matrix view; if (!HeldBlockRenderer_Show) return; diff --git a/src/HeldBlockRenderer.h b/src/HeldBlockRenderer.h index d10edbab3..8fa7a807c 100644 --- a/src/HeldBlockRenderer.h +++ b/src/HeldBlockRenderer.h @@ -11,5 +11,5 @@ extern struct IGameComponent HeldBlockRenderer_Component; extern cc_bool HeldBlockRenderer_Show; void HeldBlockRenderer_ClickAnim(cc_bool digging); -void HeldBlockRenderer_Render(double delta); +void HeldBlockRenderer_Render(float delta); #endif diff --git a/src/Http_Web.c b/src/Http_Web.c index dad8300fb..c6374acec 100644 --- a/src/Http_Web.c +++ b/src/Http_Web.c @@ -70,7 +70,7 @@ static void Http_StartNextDownload(void) { String_EncodeUtf8(urlStr, &url); res = interop_DownloadAsync(urlStr, req->requestType, req->id); - + if (res) { /* interop error code -> ClassiCube error code */ if (res == 1) res = ERR_INVALID_DATA_URL; @@ -139,7 +139,7 @@ static void Http_Init(void) { Http_InitCommon(); /* If this webpage is https://, browsers deny any http:// downloading */ httpsOnly = interop_IsHttpsOnly(); - startTime = DateTime_CurrentUTC_MS(); + startTime = DateTime_CurrentUTC(); RequestList_Init(&queuedReqs); RequestList_Init(&workingReqs); diff --git a/src/Http_Worker.c b/src/Http_Worker.c index 347bbf4ef..5bf547030 100644 --- a/src/Http_Worker.c +++ b/src/Http_Worker.c @@ -179,6 +179,9 @@ static const cc_string curlAlt = String_FromConst("libcurl.so"); #elif defined CC_BUILD_SERENITY static const cc_string curlLib = String_FromConst("/usr/local/lib/libcurl.so"); static const cc_string curlAlt = String_FromConst("/usr/local/lib/libcurl.so"); +#elif defined CC_BUILD_OS2 +static const cc_string curlLib = String_FromConst("/@unixroot/usr/lib/curl4.dll"); +static const cc_string curlAlt = String_FromConst("/@unixroot/usr/local/lib/curl4.dll"); #else static const cc_string curlLib = String_FromConst("libcurl.so.4"); static const cc_string curlAlt = String_FromConst("libcurl.so.3"); @@ -186,10 +189,17 @@ static const cc_string curlAlt = String_FromConst("libcurl.so.3"); static cc_bool LoadCurlFuncs(void) { static const struct DynamicLibSym funcs[] = { +#if !defined CC_BUILD_OS2 DynamicLib_Sym(curl_global_init), DynamicLib_Sym(curl_global_cleanup), DynamicLib_Sym(curl_easy_init), DynamicLib_Sym(curl_easy_perform), DynamicLib_Sym(curl_easy_setopt), DynamicLib_Sym(curl_easy_cleanup), DynamicLib_Sym(curl_slist_free_all), DynamicLib_Sym(curl_slist_append) +#else + DynamicLib_SymC(curl_global_init), DynamicLib_SymC(curl_global_cleanup), + DynamicLib_SymC(curl_easy_init), DynamicLib_SymC(curl_easy_perform), + DynamicLib_SymC(curl_easy_setopt), DynamicLib_SymC(curl_easy_cleanup), + DynamicLib_SymC(curl_slist_free_all), DynamicLib_SymC(curl_slist_append) +#endif }; cc_bool success; void* lib; @@ -225,7 +235,6 @@ static void HttpBackend_Init(void) { if (!LoadCurlFuncs()) { Logger_WarnFunc(&msg); return; } res = _curl_global_init(CURL_GLOBAL_DEFAULT); if (res) { Logger_SimpleWarn(res, "initing curl"); return; } - curl = _curl_easy_init(); if (!curl) { Logger_SimpleWarn(res, "initing curl_easy"); return; } @@ -487,13 +496,15 @@ static cc_result HttpConnection_Open(struct HttpConnection* conn, const struct H static cc_result HttpConnection_Read(struct HttpConnection* conn, cc_uint8* data, cc_uint32 count, cc_uint32* read) { if (conn->sslCtx) return SSL_Read(conn->sslCtx, data, count, read); + return Socket_Read(conn->socket, data, count, read); } -static cc_result HttpConnection_Write(struct HttpConnection* conn, const cc_uint8* data, cc_uint32 count, cc_uint32* wrote) { +static cc_result HttpConnection_Write(struct HttpConnection* conn, const cc_uint8* data, cc_uint32 count) { if (conn->sslCtx) - return SSL_Write(conn->sslCtx, data, count, wrote); - return Socket_Write(conn->socket, data, count, wrote); + return SSL_WriteAll(conn->sslCtx, data, count); + + return Socket_WriteAll(conn->socket, data, count); } @@ -612,15 +623,13 @@ static void HttpClient_Serialise(struct HttpClientState* state) { static cc_result HttpClient_SendRequest(struct HttpClientState* state) { char inputBuffer[16384]; cc_string inputMsg; - cc_uint32 wrote; String_InitArray(inputMsg, inputBuffer); state->req->meta = &inputMsg; state->req->progress = HTTP_PROGRESS_FETCHING_DATA; HttpClient_Serialise(state); - /* TODO check that wrote is >= inputMsg.length */ - return HttpConnection_Write(state->conn, (cc_uint8*)inputBuffer, inputMsg.length, &wrote); + return HttpConnection_Write(state->conn, (cc_uint8*)inputBuffer, inputMsg.length); } @@ -824,9 +833,12 @@ static cc_result HttpClient_ParseResponse(struct HttpClientState* state) { { dst = state->dataLeft > INPUT_BUFFER_LEN ? (req->data + req->size) : buffer; res = HttpConnection_Read(state->conn, dst, INPUT_BUFFER_LEN, &total); + if (res) return res; - if (res) return res; - if (total == 0) return ERR_END_OF_STREAM; + if (total == 0) { + Platform_LogConst("Http read unexpectedly returned 0"); + return ERR_END_OF_STREAM; + } if (dst != buffer) { /* When there is more than INPUT_BUFFER_LEN bytes of unread data/content, */ @@ -1155,6 +1167,24 @@ static cc_result HttpBackend_Do(struct HttpRequest* req, cc_string* url) { CFRelease(request); return result; } +#elif !defined CC_BUILD_NETWORKING +/*########################################################################################################################* +*------------------------------------------------------Null backend-------------------------------------------------------* +*#########################################################################################################################*/ +#include "Errors.h" + +static cc_bool HttpBackend_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +static void HttpBackend_Init(void) { } + +static void Http_AddHeader(struct HttpRequest* req, const char* key, const cc_string* value) { } + +static cc_result HttpBackend_Do(struct HttpRequest* req, cc_string* url) { + req->progress = 100; + return ERR_NOT_SUPPORTED; +} #endif @@ -1251,7 +1281,7 @@ static void PerformRequest(struct HttpRequest* req, cc_string* url) { end = Stopwatch_Measure(); elapsed = Stopwatch_ElapsedMS(beg, end); - Platform_Log4("HTTP: result %i (http %i) in %i ms (%i bytes)", + Platform_Log4("HTTP: result %e (http %i) in %i ms (%i bytes)", &req->result, &req->statusCode, &elapsed, &req->size); Http_FinishRequest(req); @@ -1304,8 +1334,8 @@ static void WorkerLoop(void) { /* Adds a req to the list of pending requests, waking up worker thread if needed */ static void HttpBackend_Add(struct HttpRequest* req, cc_uint8 flags) { -#ifdef CC_BUILD_PSP - /* TODO why doesn't threading work properly */ +#if defined CC_BUILD_PSP || defined CC_BUILD_NDS + /* TODO why doesn't threading work properly on PSP */ DoRequest(req); #else Mutex_Lock(pendingMutex); @@ -1334,8 +1364,7 @@ static void Http_Init(void) { pendingMutex = Mutex_Create(); processedMutex = Mutex_Create(); curRequestMutex = Mutex_Create(); - workerThread = Thread_Create(WorkerLoop); - - Thread_Start2(workerThread, WorkerLoop); + + Thread_Run(&workerThread, WorkerLoop, 128 * 1024, "HTTP"); } #endif diff --git a/src/Input.c b/src/Input.c index cf3b9320a..3934c57c1 100644 --- a/src/Input.c +++ b/src/Input.c @@ -46,7 +46,7 @@ static struct TouchPointer { long id; cc_uint8 type; int begX, begY; - TimeMS start; + double start; } touches[INPUT_MAX_POINTERS]; int Pointers_Count; @@ -120,7 +120,7 @@ void Input_AddTouch(long id, int x, int y) { touches[i].begX = x; touches[i].begY = y; - touches[i].start = DateTime_CurrentUTC_MS(); + touches[i].start = Game.Time; /* Also set last click time, otherwise quickly tapping */ /* sometimes triggers a 'delete' in InputHandler_Tick, */ /* and then another 'delete' in CheckBlockTap. */ @@ -137,8 +137,8 @@ void Input_UpdateTouch(long id, int x, int y) { TryUpdateTouch(id, x, y); } /* Quickly tapping should trigger a block place/delete */ static void CheckBlockTap(int i) { int btn, pressed; - if (DateTime_CurrentUTC_MS() > touches[i].start + 250) return; - if (touches[i].type != TOUCH_TYPE_ALL) return; + if (Game.Time > touches[i].start + 0.25) return; + if (touches[i].type != TOUCH_TYPE_ALL) return; if (Input_TapMode == INPUT_MODE_PLACE) { btn = MOUSE_RIGHT; @@ -414,6 +414,103 @@ static void KeyBind_Init(void) { } +/*########################################################################################################################* +*---------------------------------------------------------Gamepad---------------------------------------------------------* +*#########################################################################################################################*/ +#define GAMEPAD_BEG_BTN CCPAD_A +#define GAMEPAD_BTN_COUNT (INPUT_COUNT - GAMEPAD_BEG_BTN) + +int Gamepad_AxisBehaviour[2] = { AXIS_BEHAVIOUR_MOVEMENT, AXIS_BEHAVIOUR_CAMERA }; +int Gamepad_AxisSensitivity[2] = { AXIS_SENSI_NORMAL, AXIS_SENSI_NORMAL }; +static const float axis_sensiFactor[] = { 0.25f, 0.5f, 1.0f, 2.0f, 4.0f }; + +struct GamepadState { + float axisX[2], axisY[2]; + cc_bool pressed[GAMEPAD_BTN_COUNT]; + float holdtime[GAMEPAD_BTN_COUNT]; +}; +static struct GamepadState gamepads[INPUT_MAX_GAMEPADS]; + +static void Gamepad_Update(struct GamepadState* pad, float delta) { + int btn; + for (btn = 0; btn < GAMEPAD_BTN_COUNT; btn++) + { + if (!pad->pressed[btn]) continue; + pad->holdtime[btn] += delta; + if (pad->holdtime[btn] < 1.0f) continue; + + /* Held for over a second, trigger a fake press */ + pad->holdtime[btn] = 0; + Input_SetPressed(btn + GAMEPAD_BEG_BTN); + } +} + + +void Gamepad_SetButton(int port, int btn, int pressed) { + struct GamepadState* pad = &gamepads[port]; + int i; + btn -= GAMEPAD_BEG_BTN; + + /* Reset hold tracking time */ + if (pressed && !pad->pressed[btn]) pad->holdtime[btn] = 0; + pad->pressed[btn] = pressed != 0;; + + /* Set pressed if button pressed on any gamepad, to avoid constant flip flopping */ + /* between pressed and non-pressed when multiple controllers are plugged in */ + for (i = 0; i < INPUT_MAX_GAMEPADS; i++) + pressed |= gamepads[i].pressed[btn]; + + Input_SetNonRepeatable(btn + GAMEPAD_BEG_BTN, pressed); +} + +void Gamepad_SetAxis(int port, int axis, float x, float y, float delta) { + gamepads[port].axisX[axis] = x; + gamepads[port].axisY[axis] = y; + if (x == 0 && y == 0) return; + + int sensi = Gamepad_AxisSensitivity[axis]; + float scale = delta * 60.0f * axis_sensiFactor[sensi]; + Event_RaisePadAxis(&ControllerEvents.AxisUpdate, port, axis, x * scale, y * scale); +} + +void Gamepad_Tick(float delta) { + int port; + Window_ProcessGamepads(delta); + + for (port = 0; port < INPUT_MAX_GAMEPADS; port++) + { + Gamepad_Update(&gamepads[port], delta); + } +} + +static void PlayerInputPad(int port, int axis, struct LocalPlayer* p, float* xMoving, float* zMoving) { + float x, y, angle; + if (Gamepad_AxisBehaviour[axis] != AXIS_BEHAVIOUR_MOVEMENT) return; + + x = gamepads[port].axisX[axis]; + y = gamepads[port].axisY[axis]; + + if (x != 0 || y != 0) { + angle = Math_Atan2(x, y); + *xMoving = Math_CosF(angle); + *zMoving = Math_SinF(angle); + } +} + +static void PlayerInputGamepad(struct LocalPlayer* p, float* xMoving, float* zMoving) { + int port; + for (port = 0; port < INPUT_MAX_GAMEPADS; port++) + { + /* In splitscreen mode, tie a controller to a specific player*/ + if (Game_NumLocalPlayers > 1 && p->index != port) continue; + + PlayerInputPad(port, PAD_AXIS_LEFT, p, xMoving, zMoving); + PlayerInputPad(port, PAD_AXIS_RIGHT, p, xMoving, zMoving); + } +} +static struct LocalPlayerInput gamepadInput = { PlayerInputGamepad }; + + /*########################################################################################################################* *---------------------------------------------------------Hotkeys---------------------------------------------------------* *#########################################################################################################################*/ @@ -605,8 +702,11 @@ static void MouseStateUpdate(int button, cc_bool pressed) { struct Entity* p; /* defer getting the targeted entity, as it's a costly operation */ if (input_pickingId == -1) { - p = &LocalPlayer_Instance.Base; + p = &Entities.CurPlayer->Base; input_pickingId = Entities_GetClosest(p); + + if (input_pickingId == -1) + input_pickingId = ENTITIES_SELF_ID; } input_buttonsDown[button] = pressed; @@ -650,8 +750,8 @@ void InputHandler_OnScreensChanged(void) { static cc_bool TouchesSolid(BlockID b) { return Blocks.Collide[b] == COLLIDE_SOLID; } static cc_bool PushbackPlace(struct AABB* blockBB) { - struct Entity* p = &LocalPlayer_Instance.Base; - struct HacksComp* hacks = &LocalPlayer_Instance.Hacks; + struct Entity* p = &Entities.CurPlayer->Base; + struct HacksComp* hacks = &Entities.CurPlayer->Hacks; Face closestFace; cc_bool insideMap; @@ -660,7 +760,7 @@ static cc_bool PushbackPlace(struct AABB* blockBB) { struct LocationUpdate update; /* Offset position by the closest face */ - closestFace = Game_SelectedPos.Closest; + closestFace = Game_SelectedPos.closest; if (closestFace == FACE_XMAX) { pos.x = blockBB->Max.x + 0.5f; } else if (closestFace == FACE_ZMAX) { @@ -702,9 +802,10 @@ static cc_bool IntersectsOthers(Vec3 pos, BlockID block) { Vec3_Add(&blockBB.Min, &pos, &Blocks.MinBB[block]); Vec3_Add(&blockBB.Max, &pos, &Blocks.MaxBB[block]); - for (id = 0; id < ENTITIES_SELF_ID; id++) { + for (id = 0; id < ENTITIES_MAX_COUNT; id++) + { e = Entities.List[id]; - if (!e) continue; + if (!e || e == &Entities.CurPlayer->Base) continue; Entity_GetBounds(e, &entityBB); entityBB.Min.y += 1.0f / 32.0f; /* when player is exactly standing on top of ground */ @@ -714,8 +815,8 @@ static cc_bool IntersectsOthers(Vec3 pos, BlockID block) { } static cc_bool CheckIsFree(BlockID block) { - struct Entity* p = &LocalPlayer_Instance.Base; - struct HacksComp* hacks = &LocalPlayer_Instance.Hacks; + struct Entity* p = &Entities.CurPlayer->Base; + struct HacksComp* hacks = &Entities.CurPlayer->Hacks; Vec3 pos, nextPos; struct AABB blockBB, playerBB; @@ -724,10 +825,10 @@ static cc_bool CheckIsFree(BlockID block) { /* Non solid blocks (e.g. water/flowers) can always be placed on players */ if (Blocks.Collide[block] != COLLIDE_SOLID) return true; - IVec3_ToVec3(&pos, &Game_SelectedPos.TranslatedPos); + IVec3_ToVec3(&pos, &Game_SelectedPos.translatedPos); if (IntersectsOthers(pos, block)) return false; - nextPos = LocalPlayer_Instance.Base.next.pos; + nextPos = p->next.pos; Vec3_Add(&blockBB.Min, &pos, &Blocks.MinBB[block]); Vec3_Add(&blockBB.Max, &pos, &Blocks.MaxBB[block]); @@ -760,7 +861,7 @@ void InputHandler_DeleteBlock(void) { HeldBlockRenderer_ClickAnim(true); pos = Game_SelectedPos.pos; - if (!Game_SelectedPos.Valid || !World_Contains(pos.x, pos.y, pos.z)) return; + if (!Game_SelectedPos.valid || !World_Contains(pos.x, pos.y, pos.z)) return; old = World_GetBlock(pos.x, pos.y, pos.z); if (Blocks.Draw[old] == DRAW_GAS || !Blocks.CanDelete[old]) return; @@ -772,8 +873,8 @@ void InputHandler_DeleteBlock(void) { void InputHandler_PlaceBlock(void) { IVec3 pos; BlockID old, block; - pos = Game_SelectedPos.TranslatedPos; - if (!Game_SelectedPos.Valid || !World_Contains(pos.x, pos.y, pos.z)) return; + pos = Game_SelectedPos.translatedPos; + if (!Game_SelectedPos.valid || !World_Contains(pos.x, pos.y, pos.z)) return; old = World_GetBlock(pos.x, pos.y, pos.z); block = Inventory_SelectedBlock; @@ -871,7 +972,7 @@ static void InputHandler_Toggle(int key, cc_bool* target, const char* enableMsg, } cc_bool InputHandler_SetFOV(int fov) { - struct HacksComp* h = &LocalPlayer_Instance.Hacks; + struct HacksComp* h = &Entities.CurPlayer->Hacks; if (!h->Enabled || !h->CanUseThirdPerson) return false; Camera.ZoomFov = fov; @@ -887,7 +988,7 @@ cc_bool Input_HandleMouseWheel(float delta) { if (!hotbar && Camera.Active->Zoom(delta)) return true; if (!KeyBind_IsPressed(KEYBIND_ZOOM_SCROLL)) return false; - h = &LocalPlayer_Instance.Hacks; + h = &Entities.CurPlayer->Hacks; if (!h->Enabled || !h->CanUseThirdPerson) return false; if (input_fovIndex == -1.0f) input_fovIndex = (float)Camera.ZoomFov; @@ -898,7 +999,7 @@ cc_bool Input_HandleMouseWheel(float delta) { } static void InputHandler_CheckZoomFov(void* obj) { - struct HacksComp* h = &LocalPlayer_Instance.Hacks; + struct HacksComp* h = &Entities.CurPlayer->Hacks; if (!h->Enabled || !h->CanUseThirdPerson) Camera_SetFov(Camera.DefaultFov); } @@ -990,16 +1091,18 @@ static void HandleHotkeyDown(int key) { } static cc_bool HandleLocalPlayerKey(int key) { + struct LocalPlayer* p = Entities.CurPlayer; + if (KeyBind_Claims(KEYBIND_RESPAWN, key)) { - return LocalPlayer_HandleRespawn(); + return LocalPlayer_HandleRespawn(p); } else if (KeyBind_Claims(KEYBIND_SET_SPAWN, key)) { - return LocalPlayer_HandleSetSpawn(); + return LocalPlayer_HandleSetSpawn(p); } else if (KeyBind_Claims(KEYBIND_FLY, key)) { - return LocalPlayer_HandleFly(); + return LocalPlayer_HandleFly(p); } else if (KeyBind_Claims(KEYBIND_NOCLIP, key)) { - return LocalPlayer_HandleNoclip(); + return LocalPlayer_HandleNoclip(p); } else if (KeyBind_Claims(KEYBIND_JUMP, key)) { - return LocalPlayer_HandleJump(); + return LocalPlayer_HandleJump(p); } return false; } @@ -1008,28 +1111,6 @@ static cc_bool HandleLocalPlayerKey(int key) { /*########################################################################################################################* *-----------------------------------------------------Base handlers-------------------------------------------------------* *#########################################################################################################################*/ -static void OnMouseWheel(void* obj, float delta) { - struct Screen* s; - int i; - - for (i = 0; i < Gui.ScreensCount; i++) { - s = Gui_Screens[i]; - s->dirty = true; - if (s->VTABLE->HandlesMouseScroll(s, delta)) return; - } -} - -static void OnPointerMove(void* obj, int idx) { - struct Screen* s; - int i, x = Pointers[idx].x, y = Pointers[idx].y; - - for (i = 0; i < Gui.ScreensCount; i++) { - s = Gui_Screens[i]; - s->dirty = true; - if (s->VTABLE->HandlesPointerMove(s, 1 << idx, x, y)) return; - } -} - static void OnPointerDown(void* obj, int idx) { struct Screen* s; int i, x, y, mask; @@ -1080,6 +1161,7 @@ static void OnPointerUp(void* obj, int idx) { static void OnInputDown(void* obj, int key, cc_bool was) { struct Screen* s; int i; + if (Window_Main.SoftKeyboardFocus) return; #ifndef CC_BUILD_WEB if (Input_IsEscapeButton(key) && (s = Gui_GetClosable())) { @@ -1150,13 +1232,23 @@ static void OnInputUp(void* obj, int key) { } static void OnFocusChanged(void* obj) { if (!Window_Main.Focused) Input_Clear(); } + +static void PlayerInputNormal(struct LocalPlayer* p, float* xMoving, float* zMoving) { + if (KeyBind_IsPressed(KEYBIND_FORWARD)) *zMoving -= 1; + if (KeyBind_IsPressed(KEYBIND_BACK)) *zMoving += 1; + if (KeyBind_IsPressed(KEYBIND_LEFT)) *xMoving -= 1; + if (KeyBind_IsPressed(KEYBIND_RIGHT)) *xMoving += 1; +} +static struct LocalPlayerInput normalInput = { PlayerInputNormal }; + static void OnInit(void) { - Event_Register_(&PointerEvents.Moved, NULL, OnPointerMove); + LocalPlayerInput_Add(&normalInput); + LocalPlayerInput_Add(&gamepadInput); + Event_Register_(&PointerEvents.Down, NULL, OnPointerDown); Event_Register_(&PointerEvents.Up, NULL, OnPointerUp); Event_Register_(&InputEvents.Down, NULL, OnInputDown); Event_Register_(&InputEvents.Up, NULL, OnInputUp); - Event_Register_(&InputEvents.Wheel, NULL, OnMouseWheel); Event_Register_(&WindowEvents.FocusChanged, NULL, OnFocusChanged); Event_Register_(&UserEvents.HackPermsChanged, NULL, InputHandler_CheckZoomFov); diff --git a/src/Input.h b/src/Input.h index bd2c9715d..642090569 100644 --- a/src/Input.h +++ b/src/Input.h @@ -67,10 +67,6 @@ extern struct _InputState { cc_bool RawMode; /* Sources available for input (Mouse/Keyboard, Gamepad) */ cc_uint8 Sources; - /* Whether a gamepad joystick is being used to control player movement */ - cc_bool JoystickMovement; - /* Angle of the gamepad joystick being used to control player movement */ - float JoystickAngle; } Input; #define INPUT_SOURCE_NORMAL (1 << 0) @@ -102,15 +98,16 @@ void Input_Clear(void); #define Input_IsEscapeButton(btn) ((btn) == CCKEY_ESCAPE || (btn) == CCPAD_SELECT) #if defined CC_BUILD_HAIKU -/* Haiku uses ALT instead of CTRL for clipboard and stuff */ -#define Input_IsActionPressed() Input_IsAltPressed() + /* Haiku uses ALT instead of CTRL for clipboard and stuff */ + #define Input_IsActionPressed() Input_IsAltPressed() #elif defined CC_BUILD_DARWIN -/* macOS uses CMD instead of CTRL for clipboard and stuff */ -#define Input_IsActionPressed() Input_IsWinPressed() + /* macOS uses CMD instead of CTRL for clipboard and stuff */ + #define Input_IsActionPressed() Input_IsWinPressed() #else -#define Input_IsActionPressed() Input_IsCtrlPressed() + #define Input_IsActionPressed() Input_IsCtrlPressed() #endif + #ifdef CC_BUILD_TOUCH #define INPUT_MAX_POINTERS 32 enum INPUT_MODE { INPUT_MODE_PLACE, INPUT_MODE_DELETE, INPUT_MODE_NONE, INPUT_MODE_COUNT }; @@ -184,6 +181,24 @@ CC_API cc_bool KeyBind_IsPressed(KeyBind binding); /* Set the key that the given key binding is bound to. (also updates options list) */ void KeyBind_Set(KeyBind binding, int key, cc_uint8* binds); + +/* Gamepad axes. Default behaviour is: */ +/* - left axis: player movement */ +/* - right axis: camera movement */ +enum PAD_AXIS { PAD_AXIS_LEFT, PAD_AXIS_RIGHT }; +enum AXIS_SENSITIVITY { AXIS_SENSI_LOWER, AXIS_SENSI_LOW, AXIS_SENSI_NORMAL, AXIS_SENSI_HIGH, AXIS_SENSI_HIGHER }; +enum AXIS_BEHAVIOUR { AXIS_BEHAVIOUR_MOVEMENT, AXIS_BEHAVIOUR_CAMERA }; +extern int Gamepad_AxisBehaviour[2]; +extern int Gamepad_AxisSensitivity[2]; + +/* Sets value of the given gamepad button */ +void Gamepad_SetButton(int port, int btn, int pressed); +/* Sets value of the given axis */ +void Gamepad_SetAxis(int port, int axis, float x, float y, float delta); +void Gamepad_Tick(float delta); +#define INPUT_MAX_GAMEPADS 4 + + /* whether to leave text input open for user to enter further input */ #define HOTKEY_FLAG_STAYS_OPEN 0x01 /* Whether the hotkey was auto defined (e.g. by server) */ @@ -219,6 +234,7 @@ void StoredHotkeys_Remove(int trigger, cc_uint8 modifiers); /* Adds the given hotkey from options. */ void StoredHotkeys_Add(int trigger, cc_uint8 modifiers, cc_bool moreInput, const cc_string* text); + cc_bool InputHandler_SetFOV(int fov); cc_bool Input_HandleMouseWheel(float delta); void InputHandler_Tick(void); @@ -226,20 +242,4 @@ void InputHandler_OnScreensChanged(void); void InputHandler_DeleteBlock(void); void InputHandler_PlaceBlock(void); void InputHandler_PickBlock(void); - -/* Enumeration of on-screen buttons for touch GUI */ -#define ONSCREEN_BTN_CHAT (1 << 0) -#define ONSCREEN_BTN_LIST (1 << 1) -#define ONSCREEN_BTN_SPAWN (1 << 2) -#define ONSCREEN_BTN_SETSPAWN (1 << 3) -#define ONSCREEN_BTN_FLY (1 << 4) -#define ONSCREEN_BTN_NOCLIP (1 << 5) -#define ONSCREEN_BTN_SPEED (1 << 6) -#define ONSCREEN_BTN_HALFSPEED (1 << 7) -#define ONSCREEN_BTN_CAMERA (1 << 8) -#define ONSCREEN_BTN_DELETE (1 << 9) -#define ONSCREEN_BTN_PICK (1 << 10) -#define ONSCREEN_BTN_PLACE (1 << 11) -#define ONSCREEN_BTN_SWITCH (1 << 12) -#define ONSCREEN_MAX_BTNS 13 #endif diff --git a/src/IsometricDrawer.c b/src/IsometricDrawer.c index 1cfef4faf..83a7ce48a 100644 --- a/src/IsometricDrawer.c +++ b/src/IsometricDrawer.c @@ -59,10 +59,10 @@ static void IsometricDrawer_Flat(BlockID block, float size) { minY = iso_posY - size; maxY = iso_posY + size; v.z = 0.0f; - v.x = minX; v.y = minY; v.U = rec.U1; v.V = rec.V1; *iso_vertices++ = v; - v.y = maxY; v.V = rec.V2; *iso_vertices++ = v; - v.x = maxX; v.U = rec.U2; *iso_vertices++ = v; - v.y = minY; v.V = rec.V1; *iso_vertices++ = v; + v.x = minX; v.y = minY; v.U = rec.u1; v.V = rec.v1; *iso_vertices++ = v; + v.y = maxY; v.V = rec.v2; *iso_vertices++ = v; + v.x = maxX; v.U = rec.u2; *iso_vertices++ = v; + v.y = minY; v.V = rec.v1; *iso_vertices++ = v; } static void IsometricDrawer_Angled(BlockID block, float size) { diff --git a/src/LBackend.c b/src/LBackend.c index 28f7610b2..5d18efd0b 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -24,13 +24,16 @@ #include "Input.h" #include "Utils.h" #include "Event.h" +#include "Stream.h" +#include "Logger.h" +#include "Errors.h" struct FontDesc titleFont, textFont, hintFont, logoFont, rowFont; /* Contains the pixels that are drawn to the window */ static struct Context2D framebuffer; /* The area/region of the window that needs to be redrawn and presented to the screen. */ /* If width is 0, means no area needs to be redrawn. */ -static Rect2D dirty_rect; +Rect2D dirty_rect; static cc_uint8 pendingRedraw; #define REDRAW_ALL 0x02 @@ -102,6 +105,36 @@ void LBackend_DrawTitle(struct Context2D* ctx, const char* title) { Launcher_DrawTitle(&logoFont, title, ctx); } +/* Scales up flag bitmap if necessary */ +static void LBackend_ScaleFlag(struct Bitmap* bmp) { + struct Bitmap scaled; + int width = Display_ScaleX(bmp->width); + int height = Display_ScaleY(bmp->height); + /* at default DPI don't need to rescale it */ + if (width == bmp->width && height == bmp->height) return; + + Bitmap_TryAllocate(&scaled, width, height); + if (!scaled.scan0) { + Logger_SysWarn(ERR_OUT_OF_MEMORY, "resizing flags bitmap"); return; + } + + Bitmap_Scale(&scaled, bmp, 0, 0, bmp->width, bmp->height); + Mem_Free(bmp->scan0); + *bmp = scaled; +} + +void LBackend_DecodeFlag(struct Flag* flag, cc_uint8* data, cc_uint32 len) { + struct Stream s; + cc_result res; + + Stream_ReadonlyMemory(&s, data, len); + res = Png_Decode(&flag->bmp, &s); + if (res) Logger_SysWarn(res, "decoding flag"); + flag->meta = NULL; + + LBackend_ScaleFlag(&flag->bmp); +} + static void OnPointerMove(void* obj, int idx); void LBackend_SetScreen(struct LScreen* s) { int i; @@ -154,8 +187,8 @@ void LBackend_MarkDirty(void* widget) { /* Marks the entire window as needing to be redrawn. */ static CC_NOINLINE void MarkAllDirty(void) { - dirty_rect.x = 0; dirty_rect.Width = framebuffer.width; - dirty_rect.y = 0; dirty_rect.Height = framebuffer.height; + dirty_rect.x = 0; dirty_rect.width = framebuffer.width; + dirty_rect.y = 0; dirty_rect.height = framebuffer.height; } /* Marks the given area/region as needing to be redrawn. */ @@ -164,19 +197,19 @@ static CC_NOINLINE void MarkAreaDirty(int x, int y, int width, int height) { if (!Drawer2D_Clamp(&framebuffer, &x, &y, &width, &height)) return; /* union with existing dirty area */ - if (dirty_rect.Width) { + if (dirty_rect.width) { x1 = min(x, dirty_rect.x); y1 = min(y, dirty_rect.y); - x2 = max(x + width, dirty_rect.x + dirty_rect.Width); - y2 = max(y + height, dirty_rect.y + dirty_rect.Height); + x2 = max(x + width, dirty_rect.x + dirty_rect.width); + y2 = max(y + height, dirty_rect.y + dirty_rect.height); x = x1; width = x2 - x1; y = y1; height = y2 - y1; } - dirty_rect.x = x; dirty_rect.Width = width; - dirty_rect.y = y; dirty_rect.Height = height; + dirty_rect.x = x; dirty_rect.width = width; + dirty_rect.y = y; dirty_rect.height = height; } void LBackend_InitFramebuffer(void) { @@ -212,8 +245,8 @@ static void DrawBoxBounds(BitmapCol color, int x, int y, int width, int height) } static CC_NOINLINE void DrawWidget(struct LWidget* w) { - w->last.x = w->x; w->last.Width = w->width; - w->last.y = w->y; w->last.Height = w->height; + w->last.x = w->x; w->last.width = w->width; + w->last.y = w->y; w->last.height = w->height; w->dirty = false; w->VTABLE->Draw(w); @@ -241,10 +274,10 @@ static CC_NOINLINE void RedrawDirty(void) { if (!w->dirty) continue; /* check if widget might need redrawing of background behind */ - if (!w->opaque || w->last.Width > w->width || w->last.Height > w->height) { + if (!w->opaque || w->last.width > w->width || w->last.height > w->height) { s->ResetArea(&framebuffer, - w->last.x, w->last.y, w->last.Width, w->last.Height); - MarkAreaDirty(w->last.x, w->last.y, w->last.Width, w->last.Height); + w->last.x, w->last.y, w->last.width, w->last.height); + MarkAreaDirty(w->last.x, w->last.y, w->last.width, w->last.height); } DrawWidget(w); } @@ -268,11 +301,13 @@ void LBackend_ThemeChanged(void) { LBackend_Redraw(); } void LBackend_Tick(void) { DoRedraw(); - if (!dirty_rect.Width) return; + if (!dirty_rect.width) return; - Window_DrawFramebuffer(dirty_rect, &framebuffer.bmp); - dirty_rect.x = 0; dirty_rect.Width = 0; - dirty_rect.y = 0; dirty_rect.Height = 0; + OnscreenKeyboard_Draw2D(&dirty_rect, &framebuffer.bmp); + Window_DrawFramebuffer(dirty_rect, &framebuffer.bmp); + + dirty_rect.x = 0; dirty_rect.width = 0; + dirty_rect.y = 0; dirty_rect.height = 0; } @@ -297,6 +332,7 @@ static void OnPointerDown(void* obj, int idx) { struct LScreen* s = Launcher_Active; struct LWidget* over; struct LWidget* prev; + if (Window_Main.SoftKeyboardFocus) return; if (!s) return; over = GetWidgetAt(s, idx); @@ -310,6 +346,7 @@ static void OnPointerUp(void* obj, int idx) { struct LScreen* s = Launcher_Active; struct LWidget* over; struct LWidget* prev; + if (Window_Main.SoftKeyboardFocus) return; if (!s) return; over = GetWidgetAt(s, idx); @@ -330,6 +367,7 @@ static void OnPointerMove(void* obj, int idx) { struct LWidget* over; struct LWidget* prev; cc_bool overSame; + if (Window_Main.SoftKeyboardFocus) return; if (!s) return; over = GetWidgetAt(s, idx); @@ -534,9 +572,9 @@ void LBackend_CheckboxDraw(struct LCheckbox* w) { /*########################################################################################################################* *------------------------------------------------------InputWidget--------------------------------------------------------* *#########################################################################################################################*/ -static TimeMS caretStart; +static cc_uint64 caretStart; static Rect2D caretRect, lastCaretRect; -#define Rect2D_Equals(a, b) a.x == b.x && a.y == b.y && a.Width == b.Width && a.Height == b.Height +#define Rect2D_Equals(a, b) a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height void LBackend_InputInit(struct LInput* w, int width) { w->width = Display_ScaleX(width); @@ -568,17 +606,17 @@ static Rect2D LInput_MeasureCaret(struct LInput* w, cc_string* text) { DrawTextArgs_Make(&args, text, &textFont, true); r.x = w->x + xInputOffset; - r.y = w->y + w->height - caretOffset; r.Height = caretHeight; + r.y = w->y + w->height - caretOffset; r.height = caretHeight; if (w->caretPos == -1) { r.x += Drawer2D_TextWidth(&args); - r.Width = caretWidth; + r.width = caretWidth; } else { args.text = String_UNSAFE_Substring(text, 0, w->caretPos); r.x += Drawer2D_TextWidth(&args); args.text = String_UNSAFE_Substring(text, w->caretPos, 1); - r.Width = Drawer2D_TextWidth(&args); + r.width = Drawer2D_TextWidth(&args); } return r; } @@ -620,7 +658,7 @@ void LBackend_InputTick(struct LInput* w) { Rect2D r; if (!caretStart) return; - elapsed = (int)(DateTime_CurrentUTC_MS() - caretStart); + elapsed = Stopwatch_ElapsedMS(caretStart, Stopwatch_Measure()); caretShow = (elapsed % 1000) < 500; if (caretShow == w->caretShow) return; @@ -631,7 +669,7 @@ void LBackend_InputTick(struct LInput* w) { if (Rect2D_Equals(r, lastCaretRect)) { /* Fast path, caret is blinking in same spot */ - MarkAreaDirty(r.x, r.y, r.Width, r.Height); + MarkAreaDirty(r.x, r.y, r.width, r.height); } else { /* Slow path (new widget, caret moved, etc) */ MarkAreaDirty(w->x, w->y, w->width, w->height); @@ -641,21 +679,21 @@ void LBackend_InputTick(struct LInput* w) { void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { struct OpenKeyboardArgs args; - caretStart = DateTime_CurrentUTC_MS(); + caretStart = Stopwatch_Measure(); w->caretShow = true; LInput_MoveCaretToCursor(w, idx); LBackend_MarkDirty(w); if (wasSelected) return; OpenKeyboardArgs_Init(&args, &w->text, w->inputType); - Window_OpenKeyboard(&args); + OnscreenKeyboard_Open(&args); } void LBackend_InputUnselect(struct LInput* w) { caretStart = 0; w->caretShow = false; LBackend_MarkDirty(w); - Window_CloseKeyboard(); + OnscreenKeyboard_Close(); } @@ -753,7 +791,7 @@ void LBackend_InputDraw(struct LInput* w) { caretRect = LInput_MeasureCaret(w, &text); if (!w->caretShow) return; Context2D_Clear(&framebuffer, BITMAPCOLOR_BLACK, - caretRect.x, caretRect.y, caretRect.Width, caretRect.Height); + caretRect.x, caretRect.y, caretRect.width, caretRect.height); } @@ -897,8 +935,9 @@ static void LTable_DrawHeaderBackground(struct LTable* w) { static BitmapCol LBackend_TableRowColor(struct LTable* w, int row) { struct ServerInfo* entry = row < w->rowsCount ? LTable_Get(row) : NULL; cc_bool selected = entry && String_Equals(&entry->hash, w->selectedHash); + cc_bool featured = entry && entry->featured; - return LTable_RowColor(entry, row, selected); + return LTable_RowColor(row, selected, featured); } /* Draws background behind each row in the table */ diff --git a/src/LBackend.h b/src/LBackend.h index 3162b9d96..2ebb8ab82 100644 --- a/src/LBackend.h +++ b/src/LBackend.h @@ -15,6 +15,7 @@ struct LLabel; struct LLine; struct LSlider; struct LTable; +struct Flag; void LBackend_Init(void); void LBackend_Free(void); @@ -24,6 +25,8 @@ void LBackend_CloseScreen(struct LScreen* s); void LBackend_UpdateTitleFont(void); void LBackend_DrawTitle(struct Context2D* ctx, const char* title); +void LBackend_DecodeFlag(struct Flag* flag, cc_uint8* data, cc_uint32 len); + /* Resets pixels to default, then draws widgets of current screen over it */ void LBackend_Redraw(void); void LBackend_ThemeChanged(void); @@ -65,7 +68,6 @@ void LBackend_TableInit(struct LTable* w); void LBackend_TableUpdate(struct LTable* w); /* Adjusts Y position of rows and number of visible rows */ void LBackend_TableReposition(struct LTable* w); -void LBackend_TableFlagAdded(struct LTable* w); void LBackend_TableDraw(struct LTable* w); void LBackend_TableMouseDown(struct LTable* w, int idx); diff --git a/src/LBackend_Android.c b/src/LBackend_Android.c new file mode 100644 index 000000000..5fd15e5c2 --- /dev/null +++ b/src/LBackend_Android.c @@ -0,0 +1,614 @@ +#include "LBackend.h" +#if defined CC_BUILD_ANDROID11111111111 +#include "Launcher.h" +#include "Drawer2D.h" +#include "Window.h" +#include "LWidgets.h" +#include "String.h" +#include "Gui.h" +#include "Drawer2D.h" +#include "Launcher.h" +#include "ExtMath.h" +#include "Window.h" +#include "Funcs.h" +#include "LWeb.h" +#include "Platform.h" +#include "LScreens.h" +#include "Input.h" +#include "Utils.h" +#include "Event.h" +#include +#include +#include +#include + +struct FontDesc logoFont; + +static void HookEvents(void); +static void LBackend_InitHooks(void); +void LBackend_Init(void) { + HookEvents(); + LBackend_InitHooks(); +} + +void LBackend_Free(void) { + Font_Free(&logoFont); +} + +void LBackend_UpdateLogoFont(void) { + Font_Free(&logoFont); + Launcher_MakeLogoFont(&logoFont); +} +void LBackend_DrawLogo(struct Context2D* ctx, const char* title) { + Launcher_DrawLogo(&logoFont, title, ctx); +} + +static void LBackend_LayoutDimensions(struct LWidget* w) { + const struct LLayout* l = w->layouts + 2; + while (l->type) + { + switch (l->type) + { + case LLAYOUT_WIDTH: + w->width = WindowInfo.Width - w->x - Display_ScaleX(l->offset); + w->width = max(1, w->width); + break; + case LLAYOUT_HEIGHT: + w->height = WindowInfo.Height - w->y - Display_ScaleY(l->offset); + w->height = max(1, w->height); + break; + } + l++; + } +} + +static void LBackend_GetLayoutArgs(void* widget, jvalue* args) { + struct LWidget* w = (struct LWidget*)widget; + const struct LLayout* l = w->layouts; + + args[0].i = l[0].type & 0xFF; + args[1].i = Display_ScaleX(l[0].offset); + args[2].i = l[1].type & 0xFF; + args[3].i = Display_ScaleY(l[1].offset); +} + +void LBackend_LayoutWidget(struct LWidget* w) { + const struct LLayout* l = w->layouts; + // TODO remove this? once Table is done + + /* e.g. Table widget needs adjusts width/height based on window */ + if (l[1].type & LLAYOUT_EXTRA) + LBackend_LayoutDimensions(w); +} + +void LBackend_MarkDirty(void* widget) { } +void LBackend_InitFramebuffer(void) { } +void LBackend_FreeFramebuffer(void) { } + +static void JNICALL java_drawBackground(JNIEnv* env, jobject o, jobject bmp) { + Platform_LogConst("---$$$--"); + AndroidBitmapInfo info; + void* addr = NULL; + + AndroidBitmap_getInfo(env, bmp, &info); + AndroidBitmap_lockPixels(env, bmp, &addr); + + // TODO refactor this + struct Context2D ctx; + struct Bitmap pixels; + pixels.scan0 = addr; + pixels.width = info.width; + pixels.height = info.height; + Context2D_Wrap(&ctx, &pixels); + + struct LScreen* s = Launcher_Active; + if (s) s->DrawBackground(s, &ctx); + + AndroidBitmap_unlockPixels(env, bmp); +} + +void LBackend_Redraw(void) { + JNIEnv* env; + JavaGetCurrentEnv(env); + JavaCallVoid(env, "redrawBackground", "()V", NULL); +} + +static void LBackend_ButtonUpdateBackground(struct LButton* btn); +void LBackend_ThemeChanged(void) { + struct LScreen* s = Launcher_Active; + LBackend_Redraw(); + + for (int i = 0; i < s->numWidgets; i++) + { + struct LWidget* w = s->widgets[i]; + if (w->type != LWIDGET_BUTTON) continue; + LBackend_ButtonUpdateBackground((struct LButton*)w); + } +} + +void LBackend_Tick(void) { } + +static struct LWidget* FindWidgetForView(int id) { + struct LScreen* s = Launcher_Active; + for (int i = 0; i < s->numWidgets; i++) + { + void* meta = s->widgets[i]->meta; + if (meta != id) continue; + + return s->widgets[i]; + } + return NULL; +} + +static int ToAndroidColor(BitmapCol color) { + int R = BitmapCol_R(color); + int G = BitmapCol_G(color); + int B = BitmapCol_B(color); + int A = BitmapCol_A(color); + return (A << 24) | (R << 16) | (G << 8) | B; +} + + +static jstring JNICALL java_nextTextPart(JNIEnv* env, jobject o, jstring total, jintArray state) { + char buffer[NATIVE_STR_LEN]; + cc_string text = JavaGetString(env, total, buffer); + + jint* state_ = (*env)->GetIntArrayElements(env, state, NULL); + text.buffer += state_[0]; + text.length -= state_[0]; + + cc_string left = text, part; + char colorCode = 'f'; + + Drawer2D_UNSAFE_NextPart(&text, &part, &colorCode); + BitmapCol color = Drawer2D_GetColor(colorCode); + + state_[0] += left.length - text.length; + state_[1] = ToAndroidColor(color); + + (*env)->ReleaseIntArrayElements(env, state, state_, 0); + return JavaMakeString(env, &part); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Event handling------------------------------------------------------* +*#########################################################################################################################*/ +static void RequestRedraw(void* obj) { LBackend_Redraw(); } + +static void OnPointerUp(void* obj, int idx) { + Launcher_Active->MouseUp(Launcher_Active, idx); +} + +static void OnWindowCreated(void* obj) { + // e.g. after pause and resume + // TODO should pause/resume not trigger launcher screen recreation? + if (Launcher_Active) Launcher_SetScreen(Launcher_Active); +} + +static void HookEvents(void) { + Event_Register_(&WindowEvents.RedrawNeeded, NULL, RequestRedraw); + Event_Register_(&PointerEvents.Up, NULL, OnPointerUp); + Event_Register_(&WindowEvents.Created, NULL, OnWindowCreated); +} + + +/*########################################################################################################################* +*------------------------------------------------------ButtonWidget-------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_ButtonInit(struct LButton* w, int width, int height) { + w->_textWidth = Display_ScaleX(width); + w->_textHeight = Display_ScaleY(height); +} + +static void LBackend_ButtonShow(struct LButton* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[6]; + + LBackend_GetLayoutArgs(w, args); + args[4].i = w->_textWidth; + args[5].i = w->_textHeight; + + jmethodID method = JavaGetIMethod(env, "buttonAdd", "(IIIIII)I"); + w->meta = (void*)JavaICall_Int(env, method, args); +} + +void LBackend_ButtonUpdate(struct LButton* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[2]; + if (!w->meta) return; + + args[0].i = (int)w->meta; + args[1].l = JavaMakeString(env, &w->text); + + // TODO share logic with LabelUpdate/ButtonUpdate + jmethodID method = JavaGetIMethod(env, "buttonUpdate", "(ILjava/lang/String;)V"); + JavaICall_Void(env, method, args); + (*env)->DeleteLocalRef(env, args[1].l); +} +void LBackend_ButtonDraw(struct LButton* w) { } + +static void JNICALL java_makeButtonActive(JNIEnv* env, jobject o, jobject bmp) { + Platform_LogConst("---&&&--"); + AndroidBitmapInfo info; + void* addr = NULL; + + // TODO share code with drawBackground + AndroidBitmap_getInfo(env, bmp, &info); + AndroidBitmap_lockPixels(env, bmp, &addr); + + // TODO refactor this + struct Context2D ctx; + struct Bitmap pixels; + pixels.scan0 = addr; + pixels.width = info.width; + pixels.height = info.height; + Context2D_Wrap(&ctx, &pixels); + + LButton_DrawBackground(&ctx, 0, 0, info.width, info.height, true); + AndroidBitmap_unlockPixels(env, bmp); +} + +static void JNICALL java_makeButtonDefault(JNIEnv* env, jobject o, jobject bmp) { + Platform_LogConst("---####--"); + AndroidBitmapInfo info; + void* addr = NULL; + + // TODO share code with drawBackground + AndroidBitmap_getInfo(env, bmp, &info); + AndroidBitmap_lockPixels(env, bmp, &addr); + + // TODO refactor this + struct Context2D ctx; + struct Bitmap pixels; + pixels.scan0 = addr; + pixels.width = info.width; + pixels.height = info.height; + Context2D_Wrap(&ctx, &pixels); + + LButton_DrawBackground(&ctx, 0, 0, info.width, info.height, false); + AndroidBitmap_unlockPixels(env, bmp); +} + +static void LBackend_ButtonUpdateBackground(struct LButton* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[1]; + if (!w->meta) return; + + args[0].i = (int)w->meta; + jmethodID method = JavaGetIMethod(env, "buttonUpdateBackground", "(I)V"); + JavaICall_Void(env, method, args); +} + + +/*########################################################################################################################* +*-----------------------------------------------------CheckboxWidget------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_CheckboxInit(struct LCheckbox* w) { } + +static void LBackend_CheckboxShow(struct LCheckbox* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[6]; + + LBackend_GetLayoutArgs(w, args); + args[4].l = JavaMakeString(env, &w->text); + args[5].z = w->value; + + jmethodID method = JavaGetIMethod(env, "checkboxAdd", "(IIIILjava/lang/String;Z)I"); + w->meta = (void*)JavaICall_Int(env, method, args); + (*env)->DeleteLocalRef(env, args[4].l); +} +void LBackend_CheckboxDraw(struct LCheckbox* w) { } + +void LBackend_CheckboxUpdate(struct LCheckbox* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[2]; + if (!w->meta) return; + + args[0].i = (int)w->meta; + args[1].z = w->value; + + // TODO share logic with LabelUpdate/ButtonUpdate + jmethodID method = JavaGetIMethod(env, "checkboxUpdate", "(IZ)V"); + JavaICall_Void(env, method, args); +} + + +/*########################################################################################################################* +*------------------------------------------------------InputWidget--------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_InputInit(struct LInput* w, int width) { + w->_textHeight = Display_ScaleX(width); +} + +static void LBackend_InputShow(struct LInput* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[8]; + + LBackend_GetLayoutArgs(w, args); + args[4].i = w->_textHeight; + args[5].i = Display_ScaleY(LINPUT_HEIGHT); + args[6].i = w->inputType; + args[7].l = JavaMakeConst(env, w->hintText); + + jmethodID method = JavaGetIMethod(env, "inputAdd", "(IIIIIIILjava/lang/String;)I"); + w->meta = (void*)JavaICall_Int(env, method, args); + (*env)->DeleteLocalRef(env, args[7].l); +} + +void LBackend_InputUpdate(struct LInput* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[2]; + if (!w->meta) return; + + args[0].i = (int)w->meta; + args[1].l = JavaMakeString(env, &w->text); + + // TODO share logic with LabelUpdate/ButtonUpdate + jmethodID method = JavaGetIMethod(env, "inputUpdate", "(ILjava/lang/String;)V"); + JavaICall_Void(env, method, args); + (*env)->DeleteLocalRef(env, args[1].l); +} + +void LBackend_InputTick(struct LInput* w) { } +void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { } +void LBackend_InputUnselect(struct LInput* w) { } +void LBackend_InputDraw(struct LInput* w) { } + + +/*########################################################################################################################* +*------------------------------------------------------LabelWidget--------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_LabelInit(struct LLabel* w) { } + +static void LBackend_LabelShow(struct LLabel* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[4]; + LBackend_GetLayoutArgs(w, args); + + jmethodID method = JavaGetIMethod(env, "labelAdd", "(IIII)I"); + w->meta = (void*)JavaICall_Int(env, method, args); +} + +void LBackend_LabelUpdate(struct LLabel* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[2]; + if (!w->meta) return; + + args[0].i = (int)w->meta; + args[1].l = JavaMakeString(env, &w->text); + + // TODO share logic with LabelUpdate/ButtonUpdate + jmethodID method = JavaGetIMethod(env, "labelUpdate", "(ILjava/lang/String;)V"); + JavaICall_Void(env, method, args); + (*env)->DeleteLocalRef(env, args[1].l); +} +void LBackend_LabelDraw(struct LLabel* w) { } + + +/*########################################################################################################################* +*-------------------------------------------------------LineWidget--------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_LineInit(struct LLine* w, int width) { + w->_width = Display_ScaleX(width); +} + +static void LBackend_LineShow(struct LLine* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[7]; + + LBackend_GetLayoutArgs(w, args); + args[4].i = w->_width; + args[5].i = Display_ScaleY(LLINE_HEIGHT); + args[6].i = ToAndroidColor(LLine_GetColor()); + + jmethodID method = JavaGetIMethod(env, "lineAdd", "(IIIIIII)I"); + w->meta = (void*)JavaICall_Int(env, method, args); +} +void LBackend_LineDraw(struct LLine* w) { } + + +/*########################################################################################################################* +*------------------------------------------------------SliderWidget-------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_SliderInit(struct LSlider* w, int width, int height) { + w->_width = Display_ScaleX(width); + w->_height = Display_ScaleY(height); +} + +static void LBackend_SliderShow(struct LSlider* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[7]; + + LBackend_GetLayoutArgs(w, args); + args[4].i = w->_width; + args[5].i = w->_height; + args[6].i = ToAndroidColor(w->color); + + jmethodID method = JavaGetIMethod(env, "sliderAdd", "(IIIIIII)I"); + w->meta = (void*)JavaICall_Int(env, method, args); +} + +void LBackend_SliderUpdate(struct LSlider* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[2]; + if (!w->meta) return; + + args[0].i = (int)w->meta; + args[1].i = w->value; + + jmethodID method = JavaGetIMethod(env, "sliderUpdate", "(II)V"); + JavaICall_Void(env, method, args); +} +void LBackend_SliderDraw(struct LSlider* w) {} + + +/*########################################################################################################################* +*-------------------------------------------------------TableWidget-------------------------------------------------------* +*#########################################################################################################################*/ +void LBackend_TableInit(struct LTable* w) { + +} + +static void LBackend_TableShow(struct LTable* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[5]; + + LBackend_GetLayoutArgs(w, args); + args[4].i = ToAndroidColor(LTable_RowColor(1, false, false)); + + jmethodID method = JavaGetIMethod(env, "tableAdd", "(IIIII)I"); + w->meta = (void*)JavaICall_Int(env, method, args); + LBackend_TableUpdate(w); +} + +static jstring GetTableDetails(JNIEnv* env, struct ServerInfo* server) { + char buffer[NATIVE_STR_LEN]; + cc_string text = String_FromArray(buffer); + + String_Format2(&text, "%i/%i players, up for ", &server->players, &server->maxPlayers); + LTable_FormatUptime(&text, server->uptime); + if (server->software.length) String_Format1(&text, " | %s", &server->software); + + return JavaMakeString(env, &text); +} + +void LBackend_TableUpdate(struct LTable* w) { + JNIEnv* env; JavaGetCurrentEnv(env); + jvalue args[3]; + jmethodID method; + + method = JavaGetIMethod(env, "tableStartUpdate", "()V"); + JavaICall_Void(env, method, args); + method = JavaGetIMethod(env, "tableAddEntry", "(Ljava/lang/String;Ljava/lang/String;Z)V"); + + for (int i = 0; i < w->rowsCount; i++) + { + struct ServerInfo* info = LTable_Get(i); + args[0].l = JavaMakeString(env, &info->name); + args[1].l = GetTableDetails(env, info); + args[2].z = info->featured; + JavaICall_Void(env, method, args); + + (*env)->DeleteLocalRef(env, args[0].l); + (*env)->DeleteLocalRef(env, args[1].l); + } + + method = JavaGetIMethod(env, "tableFinishUpdate", "(I)V"); + args[0].i = (int)w->meta; + JavaICall_Void(env, method, args); +} + +void LBackend_TableReposition(struct LTable* w) { + +} + +void LBackend_TableFlagAdded(struct LTable* w) { +} + +void LBackend_TableDraw(struct LTable* w) { } +void LBackend_TableMouseDown(struct LTable* w, int idx) { } +void LBackend_TableMouseMove(struct LTable* w, int idx) { } +void LBackend_TableMouseUp(struct LTable* w, int idx) { } + +static jint JNICALL java_tableGetColor(JNIEnv* env, jobject o, jint row, jboolean selected, jboolean featured) { + return ToAndroidColor(LTable_RowColor(row, selected, featured)); +} + + +/*########################################################################################################################* +*--------------------------------------------------------UIBackend--------------------------------------------------------* +*#########################################################################################################################*/ +#define UI_EVENT_CLICKED 1 +#define UI_EVENT_CHANGED 2 + +static void JNICALL java_UIClicked(JNIEnv* env, jobject o, jint id) { + struct LWidget* w = FindWidgetForView(id); + if (!w) return; + + if (w->OnClick) w->OnClick(w); +} + +static void JNICALL java_UIChanged(JNIEnv* env, jobject o, jint id, jint val) { + struct LCheckbox* cb = (struct LCheckbox*)FindWidgetForView(id); + if (!cb) return; + + cb->value = val; + if (cb->ValueChanged) cb->ValueChanged(cb); +} + +static void JNICALL java_UIString(JNIEnv* env, jobject o, jint id, jstring str) { + struct LInput* ipt = (struct LInput*)FindWidgetForView(id); + if (!ipt) return; + + char buffer[NATIVE_STR_LEN]; + cc_string text = JavaGetString(env, str, buffer); + String_Copy(&ipt->text, &text); + if (ipt->TextChanged) ipt->TextChanged(ipt); +} + +static void ShowWidget(struct LWidget* w) { + switch (w->type) { + case LWIDGET_BUTTON: + LBackend_ButtonShow((struct LButton*)w); + LBackend_ButtonUpdate((struct LButton*)w); + break; + case LWIDGET_CHECKBOX: + LBackend_CheckboxShow((struct LCheckbox*)w); + break; + case LWIDGET_INPUT: + LBackend_InputShow((struct LInput*)w); + LBackend_InputUpdate((struct LInput*)w); + break; + case LWIDGET_LABEL: + LBackend_LabelShow((struct LLabel*)w); + LBackend_LabelUpdate((struct LLabel*)w); + break; + case LWIDGET_LINE: + LBackend_LineShow((struct LLine*)w); + break; + case LWIDGET_SLIDER: + LBackend_SliderShow((struct LSlider*)w); + break; + case LWIDGET_TABLE: + LBackend_TableShow((struct LTable*)w); + break; + } +} + +void LBackend_SetScreen(struct LScreen* s) { + for (int i = 0; i < s->numWidgets; i++) + { + ShowWidget(s->widgets[i]); + } +} + +void LBackend_CloseScreen(struct LScreen* s) { + // stop referencing widgets + for (int i = 0; i < s->numWidgets; i++) + { + s->widgets[i]->meta = NULL; + } + + JNIEnv* env; JavaGetCurrentEnv(env); + jmethodID method = JavaGetIMethod(env, "clearWidgetsAsync", "()V"); + JavaICall_Void(env, method, NULL); +} + +static const JNINativeMethod methods[] = { + { "nextTextPart", "(Ljava/lang/String;[I)Ljava/lang/String;", java_nextTextPart }, + { "drawBackground", "(Landroid/graphics/Bitmap;)V", java_drawBackground }, + { "makeButtonActive", "(Landroid/graphics/Bitmap;)V", java_makeButtonActive }, + { "makeButtonDefault", "(Landroid/graphics/Bitmap;)V", java_makeButtonDefault }, + { "processOnUIClicked", "(I)V", java_UIClicked }, + { "processOnUIChanged", "(II)V", java_UIChanged }, + { "processOnUIString", "(ILjava/lang/String;)V", java_UIString }, + { "tableGetColor", "(IZZ)I", java_tableGetColor }, +}; + +static void LBackend_InitHooks(void) { + JNIEnv* env; + JavaGetCurrentEnv(env); + JavaRegisterNatives(env, methods); +} +#endif \ No newline at end of file diff --git a/src/LScreens.c b/src/LScreens.c index ea611bf06..44c6b1904 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -18,6 +18,7 @@ #include "Utils.h" #include "LBackend.h" #include "Http.h" +#include "Game.h" #define LAYOUTS static const struct LLayout #define IsEnterButton(btn) (btn == CCKEY_ENTER || btn == CCPAD_START || btn == CCPAD_A || btn == CCKEY_KP_ENTER) @@ -652,18 +653,79 @@ void MFAScreen_SetActive(void) { } +/*########################################################################################################################* +*----------------------------------------------------------SplitScreen----------------------------------------------------* +*#########################################################################################################################*/ +#ifdef CC_BUILD_SPLITSCREEN +static struct SplitScreen { + LScreen_Layout + struct LButton btnPlayers[3], btnBack; + cc_bool signingIn; +} SplitScreen; + +#define SPLITSCREEN_MAX_WIDGETS 4 +static struct LWidget* split_widgets[SPLITSCREEN_MAX_WIDGETS]; + +LAYOUTS sps_btnPlayers2[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -120 } }; +LAYOUTS sps_btnPlayers3[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -70 } }; +LAYOUTS sps_btnPlayers4[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, -20 } }; +LAYOUTS sps_btnBack[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 170 } }; + +static void SplitScreen_Start(int players) { + static const cc_string user = String_FromConst(DEFAULT_USERNAME); + Game_NumLocalPlayers = players; + + Launcher_StartGame(&user, &String_Empty, &String_Empty, &String_Empty, &String_Empty); +} + +static void SplitScreen_Players2(void* w) { SplitScreen_Start(2); } +static void SplitScreen_Players3(void* w) { SplitScreen_Start(3); } +static void SplitScreen_Players4(void* w) { SplitScreen_Start(4); } + +static void SplitScreen_Activated(struct LScreen* s_) { + struct SplitScreen* s = (struct SplitScreen*)s_; + + LButton_Add(s, &s->btnPlayers[0], 300, 35, "2 player splitscreen", + SplitScreen_Players2, sps_btnPlayers2); + LButton_Add(s, &s->btnPlayers[1], 300, 35, "3 player splitscreen", + SplitScreen_Players3, sps_btnPlayers3); + LButton_Add(s, &s->btnPlayers[2], 300, 35, "4 player splitscreen", + SplitScreen_Players4, sps_btnPlayers4); + + LButton_Add(s, &s->btnBack, 100, 35, "Back", + SwitchToMain, sps_btnBack); +} + +void SplitScreen_SetActive(void) { + struct SplitScreen* s = &SplitScreen; + LScreen_Reset((struct LScreen*)s); + + s->widgets = split_widgets; + s->maxWidgets = Array_Elems(split_widgets); + + s->Activated = SplitScreen_Activated; + s->title = "Splitscreen mode"; + + Launcher_SetScreen((struct LScreen*)s); +} + +static void SwitchToSplitScreen(void* w) { SplitScreen_SetActive(); } +#endif + + /*########################################################################################################################* *----------------------------------------------------------MainScreen-----------------------------------------------------* *#########################################################################################################################*/ static struct MainScreen { LScreen_Layout - struct LButton btnLogin, btnResume, btnDirect, btnSPlayer, btnRegister, btnOptions, btnUpdates; + struct LButton btnLogin, btnResume, btnDirect, btnSPlayer, btnSplit; + struct LButton btnRegister, btnOptions, btnUpdates; struct LInput iptUsername, iptPassword; struct LLabel lblStatus, lblUpdate; cc_bool signingIn; } MainScreen; -#define MAINSCREEN_MAX_WIDGETS 11 +#define MAINSCREEN_MAX_WIDGETS 12 static struct LWidget* main_widgets[MAINSCREEN_MAX_WIDGETS]; LAYOUTS main_iptUsername[] = { { ANCHOR_CENTRE_MIN, -140 }, { ANCHOR_CENTRE, -120 } }; @@ -675,6 +737,7 @@ LAYOUTS main_lblStatus[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 20 } }; LAYOUTS main_btnResume[] = { { ANCHOR_CENTRE, 90 }, { ANCHOR_CENTRE, -25 } }; LAYOUTS main_btnDirect[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 60 } }; LAYOUTS main_btnSPlayer[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 110 } }; +LAYOUTS main_btnSplit[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_CENTRE, 160 } }; LAYOUTS main_btnRegister[] = { { ANCHOR_MIN, 6 }, { ANCHOR_MAX, 6 } }; LAYOUTS main_btnOptions[] = { { ANCHOR_CENTRE, 0 }, { ANCHOR_MAX, 6 } }; @@ -823,6 +886,12 @@ static void MainScreen_ApplyUpdateLabel(struct MainScreen* s) { } } +#ifdef CC_BUILD_CONSOLE +static void MainScreen_ExitApp(void* w) { + Window_Main.Exists = false; +} +#endif + static void MainScreen_Activated(struct LScreen* s_) { struct MainScreen* s = (struct MainScreen*)s_; @@ -839,11 +908,13 @@ static void MainScreen_Activated(struct LScreen* s_) { LLabel_Add(s, &s->lblStatus, "", main_lblStatus); LButton_Add(s, &s->btnDirect, 200, 35, "Direct connect", SwitchToDirectConnect, main_btnDirect); - LButton_Add(s, &s->btnSPlayer, 200, 35, "Singleplayer", + LButton_Add(s, &s->btnSPlayer, 200, 35, "Singleplayer", MainScreen_Singleplayer, main_btnSPlayer); +#ifdef CC_BUILD_SPLITSCREEN + LButton_Add(s, &s->btnSplit, 200, 35, "Splitscreen (WIP)", + SwitchToSplitScreen, main_btnSplit); +#endif - LLabel_Add(s, &s->lblUpdate, "&eChecking..", - Updater_Supported ? main_lblUpdate_N : main_lblUpdate_H); if (Process_OpenSupported) { LButton_Add(s, &s->btnRegister, 100, 35, "Register", MainScreen_Register, main_btnRegister); @@ -851,10 +922,19 @@ static void MainScreen_Activated(struct LScreen* s_) { LButton_Add(s, &s->btnOptions, 100, 35, "Options", SwitchToSettings, main_btnOptions); + +#ifdef CC_BUILD_CONSOLE + LLabel_Add(s, &s->lblUpdate, "&eChecking..", main_lblUpdate_N); + LButton_Add(s, &s->btnUpdates, 100, 35, "Exit", + MainScreen_ExitApp, main_btnUpdates); +#else + LLabel_Add(s, &s->lblUpdate, "&eChecking..", + Updater_Supported ? main_lblUpdate_N : main_lblUpdate_H); if (Updater_Supported) { LButton_Add(s, &s->btnUpdates, 100, 35, "Updates", SwitchToUpdates, main_btnUpdates); } +#endif s->btnResume.OnHover = MainScreen_ResumeHover; s->btnResume.OnUnhover = MainScreen_ResumeUnhover; @@ -1012,9 +1092,11 @@ static void CheckResourcesScreen_Next(void* w) { } static void CheckResourcesScreen_AddWidgets(struct CheckResourcesScreen* s) { + const char* line1_msg = Resources_MissingRequired ? "Some required resources weren't found" + : "Some optional resources weren't found"; s->lblStatus.small = true; - LLabel_Add(s, &s->lblLine1, "Some required resources weren't found", cres_lblLine1); + LLabel_Add(s, &s->lblLine1, line1_msg, cres_lblLine1); LLabel_Add(s, &s->lblLine2, "Okay to download?", cres_lblLine2); LLabel_Add(s, &s->lblStatus, "", cres_lblStatus); @@ -1030,7 +1112,7 @@ static void CheckResourcesScreen_Activated(struct LScreen* s_) { float size; CheckResourcesScreen_AddWidgets(s); - size = Resources_Size / 1024.0f; + size = Resources_MissingSize / 1024.0f; String_InitArray(str, buffer); String_Format1(&str, "&eDownload size: %f2 megabytes", &size); LLabel_SetText(&s->lblStatus, &str); @@ -1121,7 +1203,7 @@ static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, in String_InitArray(str, strBuffer); count = Fetcher_Downloaded + 1; - String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_Count); + String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_MissingCount); if (String_Equals(&str, &s->lblStatus.text)) return; LLabel_SetText(&s->lblStatus, &str); @@ -1298,12 +1380,8 @@ static void ServersScreen_Activated(struct LScreen* s_) { static void ServersScreen_Tick(struct LScreen* s_) { struct ServersScreen* s = (struct ServersScreen*)s_; - int count; LScreen_Tick(s_); - - count = FetchFlagsTask.count; LWebTask_Tick(&FetchFlagsTask.Base, NULL); - if (count != FetchFlagsTask.count) LBackend_TableFlagAdded(&s->table); if (!FetchServersTask.Base.working) return; LWebTask_Tick(&FetchServersTask.Base, NULL); @@ -1600,9 +1678,8 @@ static void UpdatesScreen_Format(struct LLabel* lbl, const char* prefix, cc_uint if (!timestamp) { String_AppendConst(&str, "&cCheck failed"); } else { - now = DateTime_CurrentUTC_MS() - UNIX_EPOCH; - /* must divide as cc_uint64, int delta overflows after 26 days */ - delta = (int)((now / 1000) - timestamp); + now = DateTime_CurrentUTC() - UNIX_EPOCH_SECONDS; + delta = (int)(now - timestamp); UpdatesScreen_FormatTime(&str, delta); } LLabel_SetText(lbl, &str); diff --git a/src/LWeb.c b/src/LWeb.c index 3cf6124a7..95bd05d4c 100644 --- a/src/LWeb.c +++ b/src/LWeb.c @@ -11,6 +11,7 @@ #include "Errors.h" #include "Utils.h" #include "Http.h" +#include "LBackend.h" /*########################################################################################################################* *----------------------------------------------------------JSON-----------------------------------------------------------* @@ -219,6 +220,7 @@ static void LWebTask_Reset(struct LWebTask* task) { void LWebTask_Tick(struct LWebTask* task, LWebTask_ErrorCallback errorCallback) { struct HttpRequest item; + if (task->completed) return; if (!Http_GetResult(task->reqID, &item)) return; @@ -579,39 +581,13 @@ void FetchUpdateTask_Run(cc_bool release, int buildIndex) { *#########################################################################################################################*/ struct FetchFlagsData FetchFlagsTask; static int flagsCount, flagsCapacity; - static struct Flag* flags; -/* Scales up flag bitmap if necessary */ -static void FetchFlagsTask_Scale(struct Bitmap* bmp) { - struct Bitmap scaled; - int width = Display_ScaleX(bmp->width); - int height = Display_ScaleY(bmp->height); - /* at default DPI don't need to rescale it */ - if (width == bmp->width && height == bmp->height) return; - - Bitmap_TryAllocate(&scaled, width, height); - if (!scaled.scan0) { - Logger_SysWarn(ERR_OUT_OF_MEMORY, "resizing flags bitmap"); return; - } - - Bitmap_Scale(&scaled, bmp, 0, 0, bmp->width, bmp->height); - Mem_Free(bmp->scan0); - *bmp = scaled; -} - static void FetchFlagsTask_DownloadNext(void); static void FetchFlagsTask_Handle(cc_uint8* data, cc_uint32 len) { struct Flag* flag = &flags[FetchFlagsTask.count]; - struct Stream s; - cc_result res; - - Stream_ReadonlyMemory(&s, data, len); - res = Png_Decode(&flag->bmp, &s); - if (res) Logger_SysWarn(res, "decoding flag"); - flag->meta = NULL; - - FetchFlagsTask_Scale(&flag->bmp); + LBackend_DecodeFlag(flag, data, len); + FetchFlagsTask.count++; FetchFlagsTask_DownloadNext(); } @@ -644,7 +620,8 @@ static void FetchFlagsTask_Ensure(void) { void FetchFlagsTask_Add(const struct ServerInfo* server) { int i; - for (i = 0; i < flagsCount; i++) { + for (i = 0; i < flagsCount; i++) + { if (flags[i].country[0] != server->country[0]) continue; if (flags[i].country[1] != server->country[1]) continue; /* flag is already or will be downloaded */ @@ -655,6 +632,7 @@ void FetchFlagsTask_Add(const struct ServerInfo* server) { Bitmap_Init(flags[flagsCount].bmp, 0, 0, NULL); flags[flagsCount].country[0] = server->country[0]; flags[flagsCount].country[1] = server->country[1]; + flags[flagsCount].meta = NULL; flagsCount++; FetchFlagsTask_DownloadNext(); @@ -662,7 +640,8 @@ void FetchFlagsTask_Add(const struct ServerInfo* server) { struct Flag* Flags_Get(const struct ServerInfo* server) { int i; - for (i = 0; i < FetchFlagsTask.count; i++) { + for (i = 0; i < FetchFlagsTask.count; i++) + { if (flags[i].country[0] != server->country[0]) continue; if (flags[i].country[1] != server->country[1]) continue; return &flags[i]; diff --git a/src/LWidgets.c b/src/LWidgets.c index 3893bf907..53c80390a 100644 --- a/src/LWidgets.c +++ b/src/LWidgets.c @@ -32,6 +32,38 @@ void LWidget_CalcOffsets(void) { flagYOffset = Display_ScaleY(6); } +static void LWidget_DrawInsetBorder(struct Context2D* ctx, BitmapCol color, int insetX, int insetY, + int x, int y, int width, int height) { + Context2D_Clear(ctx, color, + x + insetX, y, + width - 2 * insetX, insetY); + Context2D_Clear(ctx, color, + x + insetX, y + height - insetY, + width - 2 * insetX, insetY); + Context2D_Clear(ctx, color, + x, y + insetY, + insetX, height - 2 * insetY); + Context2D_Clear(ctx, color, + x + width - insetX, y + insetY, + insetX, height - 2 * insetY); +} + +void LWidget_DrawBorder(struct Context2D* ctx, BitmapCol color, int borderX, int borderY, + int x, int y, int width, int height) { + Context2D_Clear(ctx, color, + x, y, + width, borderY); + Context2D_Clear(ctx, color, + x, y + height - borderY, + width, borderY); + Context2D_Clear(ctx, color, + x, y, + borderX, height); + Context2D_Clear(ctx, color, + x + width - borderX, y, + borderX, height); +} + /*########################################################################################################################* *------------------------------------------------------ButtonWidget-------------------------------------------------------* @@ -54,23 +86,10 @@ static void LButton_DrawBase(struct Context2D* ctx, int x, int y, int width, int static void LButton_DrawBorder(struct Context2D* ctx, int x, int y, int width, int height) { BitmapCol backColor = Launcher_Theme.ButtonBorderColor; #ifdef CC_BUILD_IOS - int xoff = 0; /* TODO temp hack */ + LWidget_DrawBorder(ctx, backColor, oneX, oneY, x, y, width, height); #else - int xoff = oneX; + LWidget_DrawInsetBorder(ctx, backColor, oneX, oneY, x, y, width, height); #endif - - Context2D_Clear(ctx, backColor, - x + xoff, y, - width - 2 * xoff, oneY); - Context2D_Clear(ctx, backColor, - x + xoff, y + height - oneY, - width - 2 * xoff, oneY); - Context2D_Clear(ctx, backColor, - x, y + oneY, - oneX, height - twoY); - Context2D_Clear(ctx, backColor, - x + width - oneX, y + oneY, - oneX, height - twoY); } static void LButton_DrawHighlight(struct Context2D* ctx, int x, int y, int width, int height, cc_bool active) { @@ -335,6 +354,7 @@ void LInput_Add(void* screen, struct LInput* w, int width, const char* hintText, w->opaque = true; w->layouts = layouts; + /* Preserve existing input across Add calls */ if (!w->text.buffer) { String_InitArray(w->text, w->_textBuffer); } @@ -753,14 +773,14 @@ void LTable_ShowSelected(struct LTable* w) { LTable_ClampTopRow(w); } -BitmapCol LTable_RowColor(struct ServerInfo* entry, int row, cc_bool selected) { +BitmapCol LTable_RowColor(int row, cc_bool selected, cc_bool featured) { BitmapCol featSelColor = BitmapColor_RGB( 50, 53, 0); BitmapCol featuredColor = BitmapColor_RGB(101, 107, 0); BitmapCol selectedColor = BitmapColor_RGB( 40, 40, 40); - if (entry && entry->featured) { + if (featured) { return selected ? featSelColor : featuredColor; - } else if (entry && selected) { + } else if (selected) { return selectedColor; } diff --git a/src/LWidgets.h b/src/LWidgets.h index 96c2ed3e4..64be2e10e 100644 --- a/src/LWidgets.h +++ b/src/LWidgets.h @@ -108,7 +108,7 @@ struct LInput { int caretPos; cc_string text; int _textHeight; - char _textBuffer[STRING_SIZE]; + char _textBuffer[STRING_SIZE * 2]; }; CC_NOINLINE void LInput_Add(void* screen, struct LInput* w, int width, const char* hintText, const struct LLayout* layouts); @@ -247,5 +247,5 @@ int LTable_GetSelectedIndex(struct LTable* w); void LTable_SetSelectedTo(struct LTable* w, int index); void LTable_RowClick(struct LTable* w, int row); /* Works out the background color of the given row */ -BitmapCol LTable_RowColor(struct ServerInfo* entry, int row, cc_bool selected); +BitmapCol LTable_RowColor(int row, cc_bool selected, cc_bool featured); #endif diff --git a/src/Launcher.c b/src/Launcher.c index 8293dc74e..4b1e7f4dc 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -36,7 +36,7 @@ static struct Bitmap dirtBmp, stoneBmp; #define TILESIZE 48 static void CloseActiveScreen(void) { - Window_CloseKeyboard(); + OnscreenKeyboard_Close(); if (!Launcher_Active) return; Launcher_Active->Deactivated(Launcher_Active); @@ -65,8 +65,7 @@ void Launcher_DisplayHttpError(struct HttpRequest* req, const char* action, cc_s if (res) { /* Non HTTP error - this is not good */ Http_LogError(action, req); - String_Format2(dst, res >= 0x80000000 ? "&cError %h when %c" : "&cError %i when %c", - &res, action); + String_Format2(dst, "&cError %e when %c", &res, action); } else if (status != 200) { String_Format2(dst, "&c%i error when %c", &status, action); } else { @@ -187,6 +186,8 @@ static cc_bool IsShutdown(int key) { } static void OnInputDown(void* obj, int key, cc_bool was) { + if (Window_Main.SoftKeyboardFocus) return; + if (IsShutdown(key)) Launcher_ShouldExit = true; Launcher_Active->KeyDown(Launcher_Active, key, was); } @@ -260,7 +261,7 @@ void Launcher_Run(void) { #ifdef CC_BUILD_RESOURCES Resources_CheckExistence(); - if (Resources_Count) { + if (Resources_MissingCount) { CheckResourcesScreen_SetActive(); } else { MainScreen_SetActive(); @@ -270,7 +271,9 @@ void Launcher_Run(void) { #endif for (;;) { - Window_ProcessEvents(10 / 1000.0); + Window_ProcessEvents(10 / 1000.0f); + Window_ProcessGamepads(10 / 1000.0f); + Gamepad_Tick(10 / 1000.0f); if (!Window_Main.Exists || Launcher_ShouldExit) break; Launcher_Active->Tick(Launcher_Active); @@ -419,6 +422,7 @@ static cc_result Launcher_ProcessZipEntry(const cc_string* path, struct Stream* struct Bitmap bmp; cc_result res; + if (String_CaselessEqualsConst(path, "default.png")) { if (hasBitmappedFont) return 0; hasBitmappedFont = false; @@ -455,7 +459,6 @@ static cc_result ExtractTexturePack(const cc_string* path) { res = Zip_Extract(&stream, Launcher_SelectZipEntry, Launcher_ProcessZipEntry); - if (res) { Logger_SysWarn(res, "extracting texture pack"); } /* No point logging error for closing readonly file */ (void)stream.Close(&stream); @@ -577,4 +580,4 @@ void Launcher_MakeTitleFont(struct FontDesc* font) { Font_Make(font, 32, FONT_FLAGS_NONE); Drawer2D.BitmappedText = false; } -#endif \ No newline at end of file +#endif diff --git a/src/Logger.c b/src/Logger.c index 7bb32070d..f1797dadd 100644 --- a/src/Logger.c +++ b/src/Logger.c @@ -8,35 +8,38 @@ #include "Utils.h" #if defined CC_BUILD_WEB -/* Can't see native CPU state with javascript */ + /* Can't see native CPU state with javascript */ #elif defined CC_BUILD_WIN -#define WIN32_LEAN_AND_MEAN -#define NOSERVICE -#define NOMCX -#define NOIME -#define CUR_PROCESS_HANDLE ((HANDLE)-1) /* GetCurrentProcess() always returns -1 */ - -#include -#include -static HANDLE curProcess = CUR_PROCESS_HANDLE; + #define WIN32_LEAN_AND_MEAN + #define NOSERVICE + #define NOMCX + #define NOIME + #define CUR_PROCESS_HANDLE ((HANDLE)-1) /* GetCurrentProcess() always returns -1 */ + + #include + #include + static HANDLE curProcess = CUR_PROCESS_HANDLE; #elif defined CC_BUILD_OPENBSD || defined CC_BUILD_HAIKU || defined CC_BUILD_SERENITY -#include -/* These operating systems don't provide sys/ucontext.h */ -/* But register constants be found from includes in */ + #include + /* These operating systems don't provide sys/ucontext.h */ + /* But register constants be found from includes in */ + #elif defined CC_BUILD_OS2 + #include + #include <386/ucontext.h> #elif defined CC_BUILD_LINUX || defined CC_BUILD_ANDROID -/* Need to define this to get REG_ constants */ -#define _GNU_SOURCE -#include -#include + /* Need to define this to get REG_ constants */ + #define _GNU_SOURCE + #include + #include #elif defined CC_BUILD_POSIX -#include -#include + #include + #include #endif + #ifdef CC_BUILD_DARWIN /* Need this to detect macOS < 10.4, and switch to NS* api instead if so */ #include #endif - /* Only show up to 50 frames in backtrace */ #define MAX_BACKTRACE_FRAMES 50 @@ -83,6 +86,7 @@ static const char* GetCCErrorDesc(cc_result res) { case PNG_ERR_REACHED_IEND: return "Incomplete PNG image data"; case PNG_ERR_NO_DATA: return "No image in PNG"; case PNG_ERR_INVALID_SCANLINE: return "Invalid PNG scanline type"; + case PNG_ERR_16BITSAMPLES: return "16 bpp PNGs unsupported"; case NBT_ERR_UNKNOWN: return "Unknown NBT tag type"; case CW_ERR_ROOT_TAG: return "Invalid root NBT tag"; @@ -122,14 +126,12 @@ static void AppendErrorDesc(cc_string* msg, cc_result res, Logger_DescribeError } void Logger_FormatWarn(cc_string* msg, cc_result res, const char* action, Logger_DescribeError describeErr) { - String_Format2(msg, res < 20000 ? "Error %i when %c" : "Error %h when %c", - &res, action); + String_Format2(msg, "Error %e when %c", &res, action); AppendErrorDesc(msg, res, describeErr); } void Logger_FormatWarn2(cc_string* msg, cc_result res, const char* action, const cc_string* path, Logger_DescribeError describeErr) { - String_Format3(msg, res < 20000 ? "Error %i when %c '%s'" : "Error %h when %c '%s'", - &res, action, path); + String_Format3(msg, "Error %e when %c '%s'", &res, action, path); AppendErrorDesc(msg, res, describeErr); } @@ -244,7 +246,7 @@ static void DumpFrame(cc_string* trace, void* addr) { cc_uintptr addr_ = (cc_uintptr)addr; String_Format1(trace, "%x", &addr_); } -#elif defined CC_BUILD_POSIX +#elif defined CC_BUILD_POSIX && !defined CC_BUILD_OS2 /* need to define __USE_GNU for dladdr */ #ifndef __USE_GNU #define __USE_GNU @@ -281,7 +283,11 @@ static void DumpFrame(cc_string* trace, void* addr) { /* - however, ReadProcessMemory expects a process handle, and so that will fail since it's given a process ID */ /* So to work around this, instead manually call ReadProcessMemory with the current process handle */ static BOOL __stdcall ReadMemCallback(HANDLE process, DWORD_PTR baseAddress, PVOID buffer, DWORD size, PDWORD numBytesRead) { - return ReadProcessMemory(CUR_PROCESS_HANDLE, (LPCVOID)baseAddress, buffer, size, numBytesRead); + SIZE_T numRead = 0; + BOOL ok = ReadProcessMemory(CUR_PROCESS_HANDLE, (LPCVOID)baseAddress, buffer, size, &numRead); + + *numBytesRead = (DWORD)numRead; /* DWORD always 32 bits */ + return ok; } static cc_uintptr spRegister; @@ -379,7 +385,7 @@ void Logger_Backtrace(cc_string* trace, void* ctx) { } #elif defined CC_BACKTRACE_BUILTIN /* Implemented later at end of the file */ -#elif defined CC_BUILD_POSIX +#elif defined CC_BUILD_POSIX && !defined CC_BUILD_OS2 #include void Logger_Backtrace(cc_string* trace, void* ctx) { void* addrs[MAX_BACKTRACE_FRAMES]; @@ -1029,7 +1035,7 @@ static LONG WINAPI UnhandledFilter(struct _EXCEPTION_POINTERS* info) { cc_uintptr addr; DWORD i, numArgs; - code = (cc_uint32)info->ExceptionRecord->ExceptionCode; + code = (cc_uint32)info->ExceptionRecord->ExceptionCode; addr = (cc_uintptr)info->ExceptionRecord->ExceptionAddress; desc = ExceptionDescribe(code); @@ -1078,7 +1084,7 @@ void Logger_Hook(void) { GetVersionExA(&osInfo); if (osInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { - curProcess = (HANDLE)GetCurrentProcessId(); + curProcess = (HANDLE)((cc_uintptr)GetCurrentProcessId()); } } #elif defined CC_BUILD_POSIX @@ -1209,7 +1215,7 @@ void Logger_Log(const cc_string* msg) { Stream_AppendFile(&logStream, &path); } - if (!logStream.Meta.File) return; + if (!logStream.meta.file) return; Stream_Write(&logStream, (const cc_uint8*)msg->buffer, msg->length); } @@ -1229,7 +1235,7 @@ static void LogCrashHeader(void) { } static void CloseLogFile(void) { - if (logStream.Meta.File) logStream.Close(&logStream); + if (logStream.meta.file) logStream.Close(&logStream); } #endif diff --git a/src/MapRenderer.c b/src/MapRenderer.c index a1a77c4d7..9a029048a 100644 --- a/src/MapRenderer.c +++ b/src/MapRenderer.c @@ -48,19 +48,19 @@ static int maxChunkUpdates; static int chunksCount; static void ChunkInfo_Reset(struct ChunkInfo* chunk, int x, int y, int z) { - chunk->CentreX = x + HALF_CHUNK_SIZE; chunk->CentreY = y + HALF_CHUNK_SIZE; - chunk->CentreZ = z + HALF_CHUNK_SIZE; + chunk->centreX = x + HALF_CHUNK_SIZE; chunk->centreY = y + HALF_CHUNK_SIZE; + chunk->centreZ = z + HALF_CHUNK_SIZE; #ifndef CC_BUILD_GL11 - chunk->Vb = 0; + chunk->vb = 0; #endif - chunk->Visible = true; chunk->Empty = false; - chunk->PendingDelete = false; chunk->AllAir = false; - chunk->DrawXMin = false; chunk->DrawXMax = false; chunk->DrawZMin = false; - chunk->DrawZMax = false; chunk->DrawYMin = false; chunk->DrawYMax = false; + chunk->visible = true; chunk->empty = false; + chunk->pendingDelete = false; chunk->allAir = false; + chunk->drawXMin = false; chunk->drawXMax = false; chunk->drawZMin = false; + chunk->drawZMax = false; chunk->drawYMin = false; chunk->drawYMax = false; - chunk->NormalParts = NULL; - chunk->TranslucentParts = NULL; + chunk->normalParts = NULL; + chunk->translucentParts = NULL; } /* Index of maximum used 1D atlas + 1 */ @@ -78,7 +78,7 @@ CC_NOINLINE static int MapRenderer_UsedAtlases(void) { /*########################################################################################################################* *-------------------------------------------------------Map rendering-----------------------------------------------------* *#########################################################################################################################*/ -static void CheckWeather(double delta) { +static void CheckWeather(float delta) { IVec3 pos; BlockID block; cc_bool outside; @@ -96,11 +96,11 @@ static void CheckWeather(double delta) { } #ifdef CC_BUILD_GL11 -#define DrawFace(face, ign) Gfx_BindVb(part.Vbs[face]); Gfx_DrawIndexedTris_T2fC4b(0, 0); +#define DrawFace(face, ign) Gfx_BindVb(part.vbs[face]); Gfx_DrawIndexedTris_T2fC4b(0, 0); #define DrawFaces(f1, f2, ign) DrawFace(f1, ign); DrawFace(f2, ign); #else -#define DrawFace(face, offset) Gfx_DrawIndexedTris_T2fC4b(part.Counts[face], offset); -#define DrawFaces(f1, f2, offset) Gfx_DrawIndexedTris_T2fC4b(part.Counts[f1] + part.Counts[f2], offset); +#define DrawFace(face, offset) Gfx_DrawIndexedTris_T2fC4b(part.counts[face], offset); +#define DrawFaces(f1, f2, offset) Gfx_DrawIndexedTris_T2fC4b(part.counts[f1] + part.counts[f2], offset); #endif #define DrawNormalFaces(minFace, maxFace) \ @@ -108,13 +108,13 @@ if (drawMin && drawMax) { \ Gfx_SetFaceCulling(true); \ DrawFaces(minFace, maxFace, offset); \ Gfx_SetFaceCulling(false); \ - Game_Vertices += (part.Counts[minFace] + part.Counts[maxFace]); \ + Game_Vertices += (part.counts[minFace] + part.counts[maxFace]); \ } else if (drawMin) { \ DrawFace(minFace, offset); \ - Game_Vertices += part.Counts[minFace]; \ + Game_Vertices += part.counts[minFace]; \ } else if (drawMax) { \ - DrawFace(maxFace, offset + part.Counts[minFace]); \ - Game_Vertices += part.Counts[maxFace]; \ + DrawFace(maxFace, offset + part.counts[minFace]); \ + Game_Vertices += part.counts[maxFace]; \ } static void RenderNormalBatch(int batch) { @@ -126,64 +126,64 @@ static void RenderNormalBatch(int batch) { for (i = 0; i < renderChunksCount; i++) { info = renderChunks[i]; - if (!info->NormalParts) continue; + if (!info->normalParts) continue; - part = info->NormalParts[batchOffset]; - if (part.Offset < 0) continue; + part = info->normalParts[batchOffset]; + if (part.offset < 0) continue; hasNormParts[batch] = true; #ifndef CC_BUILD_GL11 - Gfx_BindVb_Textured(info->Vb); + Gfx_BindVb_Textured(info->vb); #endif - offset = part.Offset + part.SpriteCount; - drawMin = info->DrawXMin && part.Counts[FACE_XMIN]; - drawMax = info->DrawXMax && part.Counts[FACE_XMAX]; + offset = part.offset + part.spriteCount; + drawMin = info->drawXMin && part.counts[FACE_XMIN]; + drawMax = info->drawXMax && part.counts[FACE_XMAX]; DrawNormalFaces(FACE_XMIN, FACE_XMAX); - offset += part.Counts[FACE_XMIN] + part.Counts[FACE_XMAX]; - drawMin = info->DrawZMin && part.Counts[FACE_ZMIN]; - drawMax = info->DrawZMax && part.Counts[FACE_ZMAX]; + offset += part.counts[FACE_XMIN] + part.counts[FACE_XMAX]; + drawMin = info->drawZMin && part.counts[FACE_ZMIN]; + drawMax = info->drawZMax && part.counts[FACE_ZMAX]; DrawNormalFaces(FACE_ZMIN, FACE_ZMAX); - offset += part.Counts[FACE_ZMIN] + part.Counts[FACE_ZMAX]; - drawMin = info->DrawYMin && part.Counts[FACE_YMIN]; - drawMax = info->DrawYMax && part.Counts[FACE_YMAX]; + offset += part.counts[FACE_ZMIN] + part.counts[FACE_ZMAX]; + drawMin = info->drawYMin && part.counts[FACE_YMIN]; + drawMax = info->drawYMax && part.counts[FACE_YMAX]; DrawNormalFaces(FACE_YMIN, FACE_YMAX); - if (!part.SpriteCount) continue; - offset = part.Offset; - count = part.SpriteCount >> 2; /* 4 per sprite */ + if (!part.spriteCount) continue; + offset = part.offset; + count = part.spriteCount >> 2; /* 4 per sprite */ Gfx_SetFaceCulling(true); /* TODO: fix to not render them all */ #ifdef CC_BUILD_GL11 - Gfx_BindVb(part.Vbs[FACE_COUNT]); + Gfx_BindVb(part.vbs[FACE_COUNT]); Gfx_DrawIndexedTris_T2fC4b(0, 0); Game_Vertices += count * 4; Gfx_SetFaceCulling(false); continue; #endif - if (info->DrawXMax || info->DrawZMin) { + if (info->drawXMax || info->drawZMin) { Gfx_DrawIndexedTris_T2fC4b(count, offset); Game_Vertices += count; } offset += count; - if (info->DrawXMin || info->DrawZMax) { + if (info->drawXMin || info->drawZMax) { Gfx_DrawIndexedTris_T2fC4b(count, offset); Game_Vertices += count; } offset += count; - if (info->DrawXMin || info->DrawZMin) { + if (info->drawXMin || info->drawZMin) { Gfx_DrawIndexedTris_T2fC4b(count, offset); Game_Vertices += count; } offset += count; - if (info->DrawXMax || info->DrawZMax) { + if (info->drawXMax || info->drawZMax) { Gfx_DrawIndexedTris_T2fC4b(count, offset); Game_Vertices += count; } Gfx_SetFaceCulling(false); } } -void MapRenderer_RenderNormal(double delta) { +void MapRenderer_RenderNormal(float delta) { int batch; if (!mapChunks) return; @@ -211,13 +211,13 @@ void MapRenderer_RenderNormal(double delta) { #define DrawTranslucentFaces(minFace, maxFace) \ if (drawMin && drawMax) { \ DrawFaces(minFace, maxFace, offset); \ - Game_Vertices += (part.Counts[minFace] + part.Counts[maxFace]); \ + Game_Vertices += (part.counts[minFace] + part.counts[maxFace]); \ } else if (drawMin) { \ DrawFace(minFace, offset); \ - Game_Vertices += part.Counts[minFace]; \ + Game_Vertices += part.counts[minFace]; \ } else if (drawMax) { \ - DrawFace(maxFace, offset + part.Counts[minFace]); \ - Game_Vertices += part.Counts[maxFace]; \ + DrawFace(maxFace, offset + part.counts[minFace]); \ + Game_Vertices += part.counts[maxFace]; \ } static void RenderTranslucentBatch(int batch) { @@ -229,34 +229,34 @@ static void RenderTranslucentBatch(int batch) { for (i = 0; i < renderChunksCount; i++) { info = renderChunks[i]; - if (!info->TranslucentParts) continue; + if (!info->translucentParts) continue; - part = info->TranslucentParts[batchOffset]; - if (part.Offset < 0) continue; + part = info->translucentParts[batchOffset]; + if (part.offset < 0) continue; hasTranParts[batch] = true; #ifndef CC_BUILD_GL11 - Gfx_BindVb_Textured(info->Vb); + Gfx_BindVb_Textured(info->vb); #endif - offset = part.Offset; - drawMin = (inTranslucent || info->DrawXMin) && part.Counts[FACE_XMIN]; - drawMax = (inTranslucent || info->DrawXMax) && part.Counts[FACE_XMAX]; + offset = part.offset; + drawMin = (inTranslucent || info->drawXMin) && part.counts[FACE_XMIN]; + drawMax = (inTranslucent || info->drawXMax) && part.counts[FACE_XMAX]; DrawTranslucentFaces(FACE_XMIN, FACE_XMAX); - offset += part.Counts[FACE_XMIN] + part.Counts[FACE_XMAX]; - drawMin = (inTranslucent || info->DrawZMin) && part.Counts[FACE_ZMIN]; - drawMax = (inTranslucent || info->DrawZMax) && part.Counts[FACE_ZMAX]; + offset += part.counts[FACE_XMIN] + part.counts[FACE_XMAX]; + drawMin = (inTranslucent || info->drawZMin) && part.counts[FACE_ZMIN]; + drawMax = (inTranslucent || info->drawZMax) && part.counts[FACE_ZMAX]; DrawTranslucentFaces(FACE_ZMIN, FACE_ZMAX); - offset += part.Counts[FACE_ZMIN] + part.Counts[FACE_ZMAX]; - drawMin = (inTranslucent || info->DrawYMin) && part.Counts[FACE_YMIN]; - drawMax = (inTranslucent || info->DrawYMax) && part.Counts[FACE_YMAX]; + offset += part.counts[FACE_ZMIN] + part.counts[FACE_ZMAX]; + drawMin = (inTranslucent || info->drawYMin) && part.counts[FACE_YMIN]; + drawMax = (inTranslucent || info->drawYMax) && part.counts[FACE_YMAX]; DrawTranslucentFaces(FACE_YMIN, FACE_YMAX); } } -void MapRenderer_RenderTranslucent(double delta) { +void MapRenderer_RenderTranslucent(float delta) { int vertices, batch; if (!mapChunks) return; @@ -310,37 +310,37 @@ static void DeleteChunk(struct ChunkInfo* info) { #ifdef CC_BUILD_GL11 int j; #else - Gfx_DeleteVb(&info->Vb); + Gfx_DeleteVb(&info->vb); #endif - info->Empty = false; info->AllAir = false; + info->empty = false; info->allAir = false; #ifdef OCCLUSION info.OcclusionFlags = 0; info.OccludedFlags = 0; #endif - if (info->NormalParts) { - ptr = info->NormalParts; + if (info->normalParts) { + ptr = info->normalParts; for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += chunksCount) { - if (ptr->Offset < 0) continue; + if (ptr->offset < 0) continue; normPartsCount[i]--; #ifdef CC_BUILD_GL11 - for (j = 0; j < CHUNKPART_MAX_VBS; j++) Gfx_DeleteVb(&ptr->Vbs[j]); + for (j = 0; j < CHUNKPART_MAX_VBS; j++) Gfx_DeleteVb(&ptr->vbs[j]); #endif } - info->NormalParts = NULL; + info->normalParts = NULL; } - if (info->TranslucentParts) { - ptr = info->TranslucentParts; + if (info->translucentParts) { + ptr = info->translucentParts; for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += chunksCount) { - if (ptr->Offset < 0) continue; + if (ptr->offset < 0) continue; tranPartsCount[i]--; #ifdef CC_BUILD_GL11 - for (j = 0; j < CHUNKPART_MAX_VBS; j++) Gfx_DeleteVb(&ptr->Vbs[j]); + for (j = 0; j < CHUNKPART_MAX_VBS; j++) Gfx_DeleteVb(&ptr->vbs[j]); #endif } - info->TranslucentParts = NULL; + info->translucentParts = NULL; } } @@ -351,24 +351,24 @@ static void BuildChunk(struct ChunkInfo* info, int* chunkUpdates) { Game.ChunkUpdates++; (*chunkUpdates)++; - info->PendingDelete = false; + info->pendingDelete = false; Builder_MakeChunk(info); - if (!info->NormalParts && !info->TranslucentParts) { - info->Empty = true; return; + if (!info->normalParts && !info->translucentParts) { + info->empty = true; return; } - if (info->NormalParts) { - ptr = info->NormalParts; + if (info->normalParts) { + ptr = info->normalParts; for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += chunksCount) { - if (ptr->Offset >= 0) normPartsCount[i]++; + if (ptr->offset >= 0) normPartsCount[i]++; } } - if (info->TranslucentParts) { - ptr = info->TranslucentParts; + if (info->translucentParts) { + ptr = info->translucentParts; for (i = 0; i < MapRenderer_1DUsedCount; i++, ptr += chunksCount) { - if (ptr->Offset >= 0) tranPartsCount[i]++; + if (ptr->offset >= 0) tranPartsCount[i]++; } } } @@ -510,7 +510,7 @@ static void RefreshBorderChunks(int maxHeight) { /*########################################################################################################################* *--------------------------------------------------Chunks updating/sorting------------------------------------------------* *#########################################################################################################################*/ -#define CHUNK_TARGET_TIME ((1.0/30) + 0.01) +#define CHUNK_TARGET_TIME ((1.0f/30) + 0.01f) static int chunksTarget = 12; static Vec3 lastCamPos; static float lastYaw, lastPitch; @@ -542,25 +542,25 @@ static int UpdateChunksAndVisibility(int* chunkUpdates) { for (i = 0; i < chunksCount; i++) { info = sortedChunks[i]; - if (info->Empty) continue; + if (info->empty) continue; distSqr = distances[i]; - noData = !info->NormalParts && !info->TranslucentParts; + noData = !info->normalParts && !info->translucentParts; /* Auto unload chunks far away chunks */ if (!noData && distSqr >= buildDistSqr + 32 * 16) { DeleteChunk(info); continue; } - noData |= info->PendingDelete; + noData |= info->pendingDelete; if (noData && distSqr <= buildDistSqr && *chunkUpdates < chunksTarget) { DeleteChunk(info); BuildChunk(info, chunkUpdates); } - info->Visible = distSqr <= renderDistSqr && - FrustumCulling_SphereInFrustum(info->CentreX, info->CentreY, info->CentreZ, 14); /* 14 ~ sqrt(3 * 8^2) */ - if (info->Visible && !info->Empty) { renderChunks[j] = info; j++; } + info->visible = distSqr <= renderDistSqr && + FrustumCulling_SphereInFrustum(info->centreX, info->centreY, info->centreZ, 14); /* 14 ~ sqrt(3 * 8^2) */ + if (info->visible && !info->empty) { renderChunks[j] = info; j++; } } return j; } @@ -575,33 +575,33 @@ static int UpdateChunksStill(int* chunkUpdates) { for (i = 0; i < chunksCount; i++) { info = sortedChunks[i]; - if (info->Empty) continue; + if (info->empty) continue; distSqr = distances[i]; - noData = !info->NormalParts && !info->TranslucentParts; + noData = !info->normalParts && !info->translucentParts; /* Auto unload chunks far away chunks */ if (!noData && distSqr >= buildDistSqr + 32 * 16) { DeleteChunk(info); continue; } - noData |= info->PendingDelete; + noData |= info->pendingDelete; if (noData && distSqr <= buildDistSqr && *chunkUpdates < chunksTarget) { DeleteChunk(info); BuildChunk(info, chunkUpdates); /* only need to update the visibility of chunks in range. */ - info->Visible = distSqr <= renderDistSqr && - FrustumCulling_SphereInFrustum(info->CentreX, info->CentreY, info->CentreZ, 14); /* 14 ~ sqrt(3 * 8^2) */ - if (info->Visible && !info->Empty) { renderChunks[j] = info; j++; } - } else if (info->Visible) { + info->visible = distSqr <= renderDistSqr && + FrustumCulling_SphereInFrustum(info->centreX, info->centreY, info->centreZ, 14); /* 14 ~ sqrt(3 * 8^2) */ + if (info->visible && !info->empty) { renderChunks[j] = info; j++; } + } else if (info->visible) { renderChunks[j] = info; j++; } } return j; } -static void UpdateChunks(double delta) { +static void UpdateChunks(float delta) { struct LocalPlayer* p; cc_bool samePos; int chunkUpdates = 0; @@ -610,7 +610,7 @@ static void UpdateChunks(double delta) { chunksTarget += delta < CHUNK_TARGET_TIME ? 1 : -1; Math_Clamp(chunksTarget, 4, maxChunkUpdates); - p = &LocalPlayer_Instance; + p = Entities.CurPlayer; samePos = Vec3_Equals(&Camera.CurrentPos, &lastCamPos) && p->Base.Pitch == lastPitch && p->Base.Yaw == lastYaw; @@ -663,7 +663,7 @@ static void UpdateSortOrder(void) { for (i = 0; i < chunksCount; i++) { info = sortedChunks[i]; /* Calculate distance to chunk centre */ - dx = info->CentreX - pos.x; dy = info->CentreY - pos.y; dz = info->CentreZ - pos.z; + dx = info->centreX - pos.x; dy = info->centreY - pos.y; dz = info->centreZ - pos.z; distances[i] = dx * dx + dy * dy + dz * dz; /* Consider these 3 chunks: */ @@ -674,9 +674,9 @@ static void UpdateSortOrder(void) { /* X : DrawXMin = true, DrawXMax = true */ /* X+1: DrawXMin = true, DrawXMax = false */ - info->DrawXMin = dx >= 0; info->DrawXMax = dx <= 0; - info->DrawZMin = dz >= 0; info->DrawZMax = dz <= 0; - info->DrawYMin = dy >= 0; info->DrawYMax = dy <= 0; + info->drawXMin = dx >= 0; info->drawXMax = dx <= 0; + info->drawZMin = dz >= 0; info->drawZMax = dz <= 0; + info->drawYMin = dy >= 0; info->drawYMax = dy <= 0; } SortMapChunks(0, chunksCount - 1); @@ -684,7 +684,7 @@ static void UpdateSortOrder(void) { /*SimpleOcclusionCulling();*/ } -void MapRenderer_Update(double delta) { +void MapRenderer_Update(float delta) { if (!mapChunks) return; UpdateSortOrder(); UpdateChunks(delta); @@ -699,9 +699,9 @@ void MapRenderer_RefreshChunk(int cx, int cy, int cz) { if (cx < 0 || cy < 0 || cz < 0 || cx >= World.ChunksX || cy >= World.ChunksY || cz >= World.ChunksZ) return; info = &mapChunks[World_ChunkPack(cx, cy, cz)]; - if (info->AllAir) return; /* do not recreate chunks completely air */ - info->Empty = false; - info->PendingDelete = true; + if (info->allAir) return; /* do not recreate chunks completely air */ + info->empty = false; + info->pendingDelete = true; } void MapRenderer_OnBlockChanged(int x, int y, int z, BlockID block) { @@ -709,7 +709,7 @@ void MapRenderer_OnBlockChanged(int x, int y, int z, BlockID block) { struct ChunkInfo* chunk; chunk = &mapChunks[World_ChunkPack(cx, cy, cz)]; - chunk->AllAir &= Blocks.Draw[block] == DRAW_GAS; + chunk->allAir &= Blocks.Draw[block] == DRAW_GAS; /* TODO: Don't lookup twice, refresh directly using chunk pointer */ MapRenderer_RefreshChunk(cx, cy, cz); } diff --git a/src/MapRenderer.h b/src/MapRenderer.h index d92746ccc..f37865675 100644 --- a/src/MapRenderer.h +++ b/src/MapRenderer.h @@ -23,49 +23,49 @@ struct ChunkPartInfo { #ifdef CC_BUILD_GL11 /* 1 VB per face, another VB for sprites */ #define CHUNKPART_MAX_VBS (FACE_COUNT + 1) - GfxResourceID Vbs[CHUNKPART_MAX_VBS]; + GfxResourceID vbs[CHUNKPART_MAX_VBS]; #endif - int Offset; /* -1 if no vertices at all */ - int SpriteCount; /* Sprite vertices count */ - cc_uint16 Counts[FACE_COUNT]; /* Counts per face */ + int offset; /* -1 if no vertices at all */ + int spriteCount; /* Sprite vertices count */ + cc_uint16 counts[FACE_COUNT]; /* Counts per face */ }; /* Describes data necessary for rendering a chunk. */ struct ChunkInfo { - cc_uint16 CentreX, CentreY, CentreZ; /* Centre coordinates of the chunk */ + cc_uint16 centreX, centreY, centreZ; /* Centre coordinates of the chunk */ - cc_uint8 Visible : 1; /* Whether chunk is visible to the player */ - cc_uint8 Empty : 1; /* Whether the chunk is empty of data */ - cc_uint8 PendingDelete : 1; /* Whether chunk is pending deletion */ - cc_uint8 AllAir : 1; /* Whether chunk is completely air */ + cc_uint8 visible : 1; /* Whether chunk is visible to the player */ + cc_uint8 empty : 1; /* Whether the chunk is empty of data */ + cc_uint8 pendingDelete : 1; /* Whether chunk is pending deletion */ + cc_uint8 allAir : 1; /* Whether chunk is completely air */ cc_uint8 : 0; /* pad to next byte*/ - cc_uint8 DrawXMin : 1; - cc_uint8 DrawXMax : 1; - cc_uint8 DrawZMin : 1; - cc_uint8 DrawZMax : 1; - cc_uint8 DrawYMin : 1; - cc_uint8 DrawYMax : 1; + cc_uint8 drawXMin : 1; + cc_uint8 drawXMax : 1; + cc_uint8 drawZMin : 1; + cc_uint8 drawZMax : 1; + cc_uint8 drawYMin : 1; + cc_uint8 drawYMax : 1; cc_uint8 : 0; /* pad to next byte */ #ifdef OCCLUSION public cc_bool Visited = false, Occluded = false; public byte OcclusionFlags, OccludedFlags, DistanceFlags; #endif #ifndef CC_BUILD_GL11 - GfxResourceID Vb; + GfxResourceID vb; #endif - struct ChunkPartInfo* NormalParts; - struct ChunkPartInfo* TranslucentParts; + struct ChunkPartInfo* normalParts; + struct ChunkPartInfo* translucentParts; }; /* Renders the meshes of non-translucent blocks in visible chunks. */ -void MapRenderer_RenderNormal(double delta); +void MapRenderer_RenderNormal(float delta); /* Renders the meshes of translucent blocks in visible chunks. */ -void MapRenderer_RenderTranslucent(double delta); +void MapRenderer_RenderTranslucent(float delta); /* Potentially updates sort order of rendered chunks. */ /* Potentially builds meshes for several nearby chunks. */ /* NOTE: This should be called once per frame. */ -void MapRenderer_Update(double delta); +void MapRenderer_Update(float delta); /* Marks the given chunk as needing to be rebuilt/redrawn. */ /* NOTE: Coordinates outside the map are simply ignored. */ diff --git a/src/Menus.c b/src/Menus.c index 22f7a48f7..623761648 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -42,37 +42,36 @@ struct MenuOptionDesc { Widget_LeftClick OnClick; Button_Get GetValue; Button_Set SetValue; }; -struct SimpleButtonDesc { short x, y; const char* title; Widget_LeftClick onClick; }; /*########################################################################################################################* *--------------------------------------------------------Menu base--------------------------------------------------------* *#########################################################################################################################*/ -static void Menu_InitButtons(struct ButtonWidget* btns, int width, const struct SimpleButtonDesc* descs, int count) { +void Menu_AddButtons(void* screen, struct ButtonWidget* btns, int width, const struct SimpleButtonDesc* descs, int count) { int i; for (i = 0; i < count; i++) { - ButtonWidget_Init(&btns[i], width, descs[i].onClick); + ButtonWidget_Add(screen, &btns[i], width, descs[i].onClick); } } -static void Menu_LayoutButtons(struct ButtonWidget* btns, const struct SimpleButtonDesc* descs, int count) { +void Menu_LayoutButtons(struct ButtonWidget* btns, const struct SimpleButtonDesc* descs, int count) { int i; for (i = 0; i < count; i++) { Widget_SetLocation(&btns[i], ANCHOR_CENTRE, ANCHOR_CENTRE, descs[i].x, descs[i].y); } } -static void Menu_SetButtons(struct ButtonWidget* btns, struct FontDesc* font, const struct SimpleButtonDesc* descs, int count) { +void Menu_SetButtons(struct ButtonWidget* btns, struct FontDesc* font, const struct SimpleButtonDesc* descs, int count) { int i; for (i = 0; i < count; i++) { ButtonWidget_SetConst(&btns[i], descs[i].title, font); } } -static void Menu_LayoutBack(struct ButtonWidget* btn) { +void Menu_LayoutBack(struct ButtonWidget* btn) { Widget_SetLocation(btn, ANCHOR_CENTRE, ANCHOR_MAX, 0, 25); } -static void Menu_CloseKeyboard(void* s) { Window_CloseKeyboard(); } +static void Menu_CloseKeyboard(void* s) { OnscreenKeyboard_Close(); } static void Menu_RenderBounds(void) { /* These were sourced by taking a screenshot of vanilla @@ -240,11 +239,12 @@ struct ListScreen; static struct ListScreen { Screen_Body struct ButtonWidget btns[LIST_SCREEN_ITEMS]; - struct ButtonWidget left, right, done, upload; + struct ButtonWidget left, right, done, action; struct FontDesc font; float wheelAcc; int currentIndex; - Widget_LeftClick EntryClick, DoneClick, UploadClick; + Widget_LeftClick EntryClick, DoneClick, ActionClick; + const char* actionText; void (*LoadEntries)(struct ListScreen* s); void (*UpdateEntry)(struct ListScreen* s, struct ButtonWidget* btn, const cc_string* text); const char* titleText; @@ -252,13 +252,7 @@ static struct ListScreen { struct StringsBuffer entries; } ListScreen; -static struct Widget* list_widgets[] = { - (struct Widget*)&ListScreen.btns[0], (struct Widget*)&ListScreen.btns[1], - (struct Widget*)&ListScreen.btns[2], (struct Widget*)&ListScreen.btns[3], - (struct Widget*)&ListScreen.btns[4], (struct Widget*)&ListScreen.left, - (struct Widget*)&ListScreen.right, (struct Widget*)&ListScreen.title, - (struct Widget*)&ListScreen.done, NULL -}; +static struct Widget* list_widgets[LIST_SCREEN_ITEMS + 4 + 1]; #define LISTSCREEN_EMPTY "-" static void ListScreen_Layout(void* screen) { @@ -270,12 +264,12 @@ static void ListScreen_Layout(void* screen) { ANCHOR_CENTRE, ANCHOR_CENTRE, 0, (i - 2) * 50); } - if (s->UploadClick && Input_TouchMode) { + if (Input_TouchMode) { Widget_SetLocation(&s->done, ANCHOR_CENTRE_MIN, ANCHOR_MAX, -150, 25); - Widget_SetLocation(&s->upload, ANCHOR_CENTRE_MAX, ANCHOR_MAX, -150, 25); + Widget_SetLocation(&s->action, ANCHOR_CENTRE_MAX, ANCHOR_MAX, -150, 25); } else { Widget_SetLocation(&s->done, ANCHOR_CENTRE, ANCHOR_MAX, 0, 25); - Widget_SetLocation(&s->upload, ANCHOR_CENTRE, ANCHOR_MAX, 0, 70); + Widget_SetLocation(&s->action, ANCHOR_CENTRE, ANCHOR_MAX, 0, 70); } Widget_SetLocation(&s->left, ANCHOR_CENTRE, ANCHOR_CENTRE, -220, 0); @@ -355,7 +349,8 @@ static void ListScreen_MoveForwards(void* screen, void* b) { } static cc_string ListScreen_UNSAFE_GetCur(struct ListScreen* s, void* widget) { - int i = Screen_Index(s, widget); + struct ButtonWidget* btn = (struct ButtonWidget*)widget; + int i = btn->meta.val; return ListScreen_UNSAFE_Get(s, s->currentIndex + i); } @@ -397,33 +392,30 @@ static void ListScreen_Init(void* screen) { struct ListScreen* s = (struct ListScreen*)screen; int i, width; s->widgets = list_widgets; - s->numWidgets = Array_Elems(list_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(list_widgets); + s->wheelAcc = 0.0f; s->currentIndex = 0; - for (i = 0; i < LIST_SCREEN_ITEMS; i++) { - ButtonWidget_Init(&s->btns[i], 300, s->EntryClick); + for (i = 0; i < LIST_SCREEN_ITEMS; i++) + { + ButtonWidget_Add(s, &s->btns[i], 300, s->EntryClick); + s->btns[i].meta.val = i; } + width = Input_TouchMode ? 140 : 400; + ButtonWidget_Add(s, &s->action, width, s->ActionClick); - width = s->UploadClick && Input_TouchMode ? 140 : 400; - ButtonWidget_Init(&s->upload, width, s->UploadClick); - ButtonWidget_Init(&s->done, width, s->DoneClick); - - if (s->UploadClick) { - s->widgets[9] = (struct Widget*)&s->upload; - } else { - s->widgets[9] = NULL; - } - - ButtonWidget_Init(&s->left, 40, ListScreen_MoveBackwards); - ButtonWidget_Init(&s->right, 40, ListScreen_MoveForwards); - TextWidget_Init(&s->title); + ButtonWidget_Add(s, &s->left, 40, ListScreen_MoveBackwards); + ButtonWidget_Add(s, &s->right, 40, ListScreen_MoveForwards); + TextWidget_Add(s, &s->title); + ButtonWidget_Add(s, &s->done, width, s->DoneClick); s->maxVertices = Screen_CalcDefaultMaxVertices(screen); s->LoadEntries(s); } -static void ListScreen_Render(void* screen, double delta) { +static void ListScreen_Render(void* screen, float delta) { Menu_RenderBounds(); Screen_Render2Widgets(screen, delta); } @@ -450,12 +442,7 @@ static void ListScreen_ContextRecreated(void* screen) { ButtonWidget_SetConst(&s->done, "Done", &s->font); ListScreen_UpdatePage(s); - if (!s->UploadClick) return; -#ifdef CC_BUILD_WEB - ButtonWidget_SetConst(&s->upload, "Upload", &s->font); -#else - ButtonWidget_SetConst(&s->upload, "Load file...", &s->font); -#endif + ButtonWidget_SetConst(&s->action, s->actionText, &s->font); } static void ListScreen_Reload(struct ListScreen* s) { @@ -483,7 +470,7 @@ void ListScreen_Show(void) { /*########################################################################################################################* *--------------------------------------------------------MenuScreen-------------------------------------------------------* *#########################################################################################################################*/ -static void MenuScreen_Render2(void* screen, double delta) { +void MenuScreen_Render2(void* screen, float delta) { Menu_RenderBounds(); Screen_Render2Widgets(screen, delta); } @@ -512,32 +499,24 @@ static void PauseScreenBase_ContextRecreated(struct PauseScreen* s, struct FontD TextWidget_SetConst(&s->title, "Game menu", titleFont); } -static void PauseScreenBase_Init(struct PauseScreen* s, int width) { - Menu_InitButtons(s->btns, width, s->descs, s->descsCount); - ButtonWidget_Init(&s->back, 400, PauseScreenBase_Game); - TextWidget_Init(&s->title); - - s->maxVertices = Screen_CalcDefaultMaxVertices(s); +static void PauseScreenBase_AddWidgets(struct PauseScreen* s, int width) { + TextWidget_Add(s, &s->title); + Menu_AddButtons(s, s->btns, width, s->descs, s->descsCount); + ButtonWidget_Add(s, &s->back, 400, PauseScreenBase_Game); } /*########################################################################################################################* *-------------------------------------------------------PauseScreen-------------------------------------------------------* *#########################################################################################################################*/ -static struct Widget* pause_widgets[] = { - (struct Widget*)&PauseScreen.title, - (struct Widget*)&PauseScreen.btns[0], (struct Widget*)&PauseScreen.btns[1], - (struct Widget*)&PauseScreen.btns[2], (struct Widget*)&PauseScreen.btns[3], - (struct Widget*)&PauseScreen.btns[4], (struct Widget*)&PauseScreen.btns[5], - (struct Widget*)&PauseScreen.back, (struct Widget*)&PauseScreen.quit -}; +static struct Widget* pause_widgets[1 + 6 + 2]; static void PauseScreen_CheckHacksAllowed(void* screen) { struct PauseScreen* s = (struct PauseScreen*)screen; if (Gui.ClassicMenu) return; Widget_SetDisabled(&s->btns[4], - !LocalPlayer_Instance.Hacks.CanAnyHacks); /* select texture pack */ + !Entities.CurPlayer->Hacks.CanAnyHacks); /* select texture pack */ s->dirty = true; } @@ -570,13 +549,15 @@ static void PauseScreen_Init(void* screen) { { -160, 50, "Hotkeys...", Menu_SwitchHotkeys } }; s->widgets = pause_widgets; - s->numWidgets = Array_Elems(pause_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(pause_widgets); Event_Register_(&UserEvents.HackPermsChanged, s, PauseScreen_CheckHacksAllowed); s->descs = descs; s->descsCount = Array_Elems(descs); - ButtonWidget_Init(&s->quit, 120, PauseScreenBase_Quit); - PauseScreenBase_Init(s, 300); + PauseScreenBase_AddWidgets(s, 300); + ButtonWidget_Add(s, &s->quit, 120, PauseScreenBase_Quit); + s->maxVertices = Screen_CalcDefaultMaxVertices(s); if (Server.IsSinglePlayer) return; s->btns[1].flags = WIDGET_FLAG_DISABLED; @@ -606,12 +587,7 @@ void PauseScreen_Show(void) { /*########################################################################################################################* *----------------------------------------------------ClassicPauseScreen---------------------------------------------------* *#########################################################################################################################*/ -static struct Widget* classicPause_widgets[] = { - (struct Widget*)&PauseScreen.title, - (struct Widget*)&PauseScreen.btns[0], (struct Widget*)&PauseScreen.btns[1], - (struct Widget*)&PauseScreen.btns[2], (struct Widget*)&PauseScreen.btns[3], - (struct Widget*)&PauseScreen.btns[4], (struct Widget*)&PauseScreen.back -}; +static struct Widget* classicPause_widgets[1 + 5 + 1]; static void ClassicPauseScreen_ContextRecreated(void* screen) { struct PauseScreen* s = (struct PauseScreen*)screen; @@ -637,13 +613,14 @@ static void ClassicPauseScreen_Init(void* screen) { { 0, 100, "Nostalgia options...", Menu_SwitchNostalgia } }; s->widgets = classicPause_widgets; - s->numWidgets = Array_Elems(classicPause_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(classicPause_widgets); s->descs = descs; /* Don't show nostalgia options in classic mode */ - s->descsCount = Game_ClassicMode ? 4 : 5; - s->widgets[5] = Game_ClassicMode ? NULL : (struct Widget*)&s->btns[4]; - PauseScreenBase_Init(s, 400); + s->descsCount = Game_ClassicMode ? 4 : 5; + PauseScreenBase_AddWidgets(s, 400); + s->maxVertices = Screen_CalcDefaultMaxVertices(s); if (Server.IsSinglePlayer) return; s->btns[1].flags = WIDGET_FLAG_DISABLED; @@ -673,17 +650,11 @@ static struct OptionsGroupScreen { Screen_Body struct FontDesc textFont; struct ButtonWidget btns[8]; - struct TextWidget desc; - struct ButtonWidget done; + struct TextWidget desc; + struct ButtonWidget done; } OptionsGroupScreen; -static struct Widget* optGroups_widgets[] = { - (struct Widget*)&OptionsGroupScreen.btns[0], (struct Widget*)&OptionsGroupScreen.btns[1], - (struct Widget*)&OptionsGroupScreen.btns[2], (struct Widget*)&OptionsGroupScreen.btns[3], - (struct Widget*)&OptionsGroupScreen.btns[4], (struct Widget*)&OptionsGroupScreen.btns[5], - (struct Widget*)&OptionsGroupScreen.btns[6], (struct Widget*)&OptionsGroupScreen.btns[7], - (struct Widget*)&OptionsGroupScreen.desc, (struct Widget*)&OptionsGroupScreen.done -}; +static struct Widget* optGroups_widgets[8 + 2]; static const char* const optsGroup_descs[8] = { "&eMusic/Sound, view bobbing, and more", @@ -709,7 +680,7 @@ static const struct SimpleButtonDesc optsGroup_btns[8] = { static void OptionsGroupScreen_CheckHacksAllowed(void* screen) { struct OptionsGroupScreen* s = (struct OptionsGroupScreen*)screen; Widget_SetDisabled(&s->btns[6], - !LocalPlayer_Instance.Hacks.CanAnyHacks); /* env settings */ + !Entities.CurPlayer->Hacks.CanAnyHacks); /* env settings */ s->dirty = true; } @@ -751,12 +722,13 @@ static void OptionsGroupScreen_Init(void* screen) { Event_Register_(&UserEvents.HackPermsChanged, s, OptionsGroupScreen_CheckHacksAllowed); s->widgets = optGroups_widgets; - s->numWidgets = Array_Elems(optGroups_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(optGroups_widgets); s->selectedI = -1; - Menu_InitButtons(s->btns, 300, optsGroup_btns, 8); - TextWidget_Init(&s->desc); - ButtonWidget_Init(&s->done, 400, Menu_SwitchPause); + Menu_AddButtons(s, s->btns, 300, optsGroup_btns, 8); + TextWidget_Add(s, &s->desc); + ButtonWidget_Add(s, &s->done, 400, Menu_SwitchPause); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -806,12 +778,7 @@ static struct EditHotkeyScreen { struct ButtonWidget btns[5], cancel; } EditHotkeyScreen; -static struct Widget* edithotkey_widgets[] = { - (struct Widget*)&EditHotkeyScreen.btns[0], (struct Widget*)&EditHotkeyScreen.btns[1], - (struct Widget*)&EditHotkeyScreen.btns[2], (struct Widget*)&EditHotkeyScreen.btns[3], - (struct Widget*)&EditHotkeyScreen.btns[4], (struct Widget*)&EditHotkeyScreen.input, - (struct Widget*)&EditHotkeyScreen.cancel -}; +static struct Widget* edithotkey_widgets[1 + 5 + 1]; static void HotkeyListScreen_MakeFlags(int flags, cc_string* str); static void EditHotkeyScreen_MakeFlags(int flags, cc_string* str) { @@ -915,7 +882,7 @@ static void EditHotkeyScreen_RemoveHotkey(void* screen, void* b) { HotkeyListScreen_Show(); } -static void EditHotkeyScreen_Render(void* screen, double delta) { +static void EditHotkeyScreen_Render(void* screen, float delta) { struct EditHotkeyScreen* s = (struct EditHotkeyScreen*)screen; PackedCol grey = PackedCol_Make(150, 150, 150, 255); @@ -987,7 +954,7 @@ static void EditHotkeyScreen_ContextRecreated(void* screen) { ButtonWidget_SetConst(&s->cancel, "Cancel", &s->titleFont); } -static void EditHotkeyScreen_Update(void* screen, double delta) { +static void EditHotkeyScreen_Update(void* screen, float delta) { struct EditHotkeyScreen* s = (struct EditHotkeyScreen*)screen; s->input.base.caretAccumulator += delta; } @@ -1018,22 +985,24 @@ static void EditHotkeyScreen_Init(void* screen) { cc_string text; s->widgets = edithotkey_widgets; - s->numWidgets = Array_Elems(edithotkey_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(edithotkey_widgets); + s->selectedI = -1; MenuInput_String(desc); - ButtonWidget_Init(&s->btns[0], 300, EditHotkeyScreen_BaseKey); - ButtonWidget_Init(&s->btns[1], 300, EditHotkeyScreen_Modifiers); - ButtonWidget_Init(&s->btns[2], 300, EditHotkeyScreen_LeaveOpen); - ButtonWidget_Init(&s->btns[3], 300, EditHotkeyScreen_SaveChanges); - ButtonWidget_Init(&s->btns[4], 300, EditHotkeyScreen_RemoveHotkey); + ButtonWidget_Add(s, &s->btns[0], 300, EditHotkeyScreen_BaseKey); + ButtonWidget_Add(s, &s->btns[1], 300, EditHotkeyScreen_Modifiers); + ButtonWidget_Add(s, &s->btns[2], 300, EditHotkeyScreen_LeaveOpen); + ButtonWidget_Add(s, &s->btns[3], 300, EditHotkeyScreen_SaveChanges); + ButtonWidget_Add(s, &s->btns[4], 300, EditHotkeyScreen_RemoveHotkey); if (s->origHotkey.trigger) { text = StringsBuffer_UNSAFE_Get(&HotkeysText, s->origHotkey.textIndex); } else { text = String_Empty; } - TextInputWidget_Create(&s->input, 500, &text, &desc); - ButtonWidget_Init(&s->cancel, 400, Menu_SwitchHotkeys); + TextInputWidget_Add(s, &s->input, 500, &text, &desc); + ButtonWidget_Add(s, &s->cancel, 400, Menu_SwitchHotkeys); s->input.onscreenPlaceholder = "Hotkey text"; s->maxVertices = Screen_CalcDefaultMaxVertices(s); @@ -1069,14 +1038,7 @@ static struct GenLevelScreen { } GenLevelScreen; #define GENLEVEL_NUM_INPUTS 4 -static struct Widget* gen_widgets[] = { - (struct Widget*)&GenLevelScreen.inputs[0], (struct Widget*)&GenLevelScreen.inputs[1], - (struct Widget*)&GenLevelScreen.inputs[2], (struct Widget*)&GenLevelScreen.inputs[3], - (struct Widget*)&GenLevelScreen.labels[0], (struct Widget*)&GenLevelScreen.labels[1], - (struct Widget*)&GenLevelScreen.labels[2], (struct Widget*)&GenLevelScreen.labels[3], - (struct Widget*)&GenLevelScreen.title, (struct Widget*)&GenLevelScreen.flatgrass, - (struct Widget*)&GenLevelScreen.vanilla, (struct Widget*)&GenLevelScreen.cancel -}; +static struct Widget* gen_widgets[2 * GENLEVEL_NUM_INPUTS + 4]; CC_NOINLINE static int GenLevelScreen_GetInt(struct GenLevelScreen* s, int index) { struct TextInputWidget* input = &s->inputs[index]; @@ -1136,17 +1098,21 @@ static void GenLevelScreen_Make(struct GenLevelScreen* s, int i, int def) { String_InitArray(tmp, tmpBuffer); desc.VTABLE->GetDefault(&desc, &tmp); - TextInputWidget_Create(&s->inputs[i], 200, &tmp, &desc); - s->inputs[i].base.showCaret = false; - TextWidget_Init(&s->labels[i]); + TextWidget_Add(s, &s->labels[i]); s->labels[i].color = PackedCol_Make(224, 224, 224, 255); + /* TODO placeholder */ + TextInputWidget_Add(s, &s->inputs[i], 200, &tmp, &desc); + s->inputs[i].base.showCaret = false; s->inputs[i].onscreenType = KEYBOARD_TYPE_INTEGER; + s->inputs[i].base.meta.val = 10000; } +#define GenLevelScreen_IsInput(w) (w)->meta.val == 10000 static struct TextInputWidget* GenLevelScreen_SelectedInput(struct GenLevelScreen* s) { - if (s->selectedI >= 0 && s->selectedI < GENLEVEL_NUM_INPUTS) - return &s->inputs[s->selectedI]; + if (s->selectedI >= 0 && GenLevelScreen_IsInput(s->widgets[s->selectedI])) { + return (struct TextInputWidget*)s->widgets[s->selectedI]; + } return NULL; } @@ -1186,7 +1152,7 @@ static int GenLevelScreen_PointerDown(void* screen, int id, int x, int y) { s->selectedI = Screen_DoPointerDown(screen, id, x, y); selected = GenLevelScreen_SelectedInput(s); - if (selected) Window_SetKeyboardText(&selected->base.text); + if (selected) OnscreenKeyboard_SetText(&selected->base.text); return TOUCH_TYPE_GUI; } @@ -1220,15 +1186,15 @@ static void GenLevelScreen_ContextRecreated(void* screen) { Font_Free(&titleFont); } -static void GenLevelScreen_Update(void* screen, double delta) { +static void GenLevelScreen_Update(void* screen, float delta) { struct GenLevelScreen* s = (struct GenLevelScreen*)screen; struct TextInputWidget* selected = GenLevelScreen_SelectedInput(s); int i; + for (i = 0; i < GENLEVEL_NUM_INPUTS; i++) { - s->inputs[i].base.showCaret = i == s->selectedI; + s->inputs[i].base.showCaret = &s->inputs[i] == selected; } - if (selected) selected->base.caretAccumulator += delta; } @@ -1249,19 +1215,20 @@ static void GenLevelScreen_Layout(void* screen) { static void GenLevelScreen_Init(void* screen) { struct GenLevelScreen* s = (struct GenLevelScreen*)screen; - s->widgets = gen_widgets; - s->numWidgets = Array_Elems(gen_widgets); - s->selectedI = -1; + s->widgets = gen_widgets; + s->numWidgets = 0; + s->maxWidgets = Array_Elems(gen_widgets); + s->selectedI = -1; GenLevelScreen_Make(s, 0, World.Width); GenLevelScreen_Make(s, 1, World.Height); GenLevelScreen_Make(s, 2, World.Length); GenLevelScreen_Make(s, 3, 0); - TextWidget_Init(&s->title); - ButtonWidget_Init(&s->flatgrass, 200, GenLevelScreen_Flatgrass); - ButtonWidget_Init(&s->vanilla, 200, GenLevelScreen_Notchy); - ButtonWidget_Init(&s->cancel, 400, Menu_SwitchPause); + TextWidget_Add(s, &s->title); + ButtonWidget_Add(s, &s->flatgrass, 200, GenLevelScreen_Flatgrass); + ButtonWidget_Add(s, &s->vanilla, 200, GenLevelScreen_Notchy); + ButtonWidget_Add(s, &s->cancel, 400, Menu_SwitchPause); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -1291,11 +1258,7 @@ static struct ClassicGenScreen { struct TextWidget title; } ClassicGenScreen; -static struct Widget* classicgen_widgets[] = { - (struct Widget*)&ClassicGenScreen.title, - (struct Widget*)&ClassicGenScreen.btns[0], (struct Widget*)&ClassicGenScreen.btns[1], - (struct Widget*)&ClassicGenScreen.btns[2], (struct Widget*)&ClassicGenScreen.cancel -}; +static struct Widget* classicgen_widgets[1 + 3 + 1]; static void ClassicGenScreen_Gen(int size) { RNGState rnd; Random_SeedFromCurrentTime(&rnd); @@ -1337,13 +1300,14 @@ static void ClassicGenScreen_Layout(void* screen) { static void ClassicGenScreen_Init(void* screen) { struct ClassicGenScreen* s = (struct ClassicGenScreen*)screen; s->widgets = classicgen_widgets; - s->numWidgets = Array_Elems(classicgen_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(classicgen_widgets); - TextWidget_Init(&s->title); - ButtonWidget_Init(&s->btns[0], 400, ClassicGenScreen_Small); - ButtonWidget_Init(&s->btns[1], 400, ClassicGenScreen_Medium); - ButtonWidget_Init(&s->btns[2], 400, ClassicGenScreen_Huge); - ButtonWidget_Init(&s->cancel, 400, Menu_SwitchPause); + TextWidget_Add(s, &s->title); + ButtonWidget_Add(s, &s->btns[0], 400, ClassicGenScreen_Small); + ButtonWidget_Add(s, &s->btns[1], 400, ClassicGenScreen_Medium); + ButtonWidget_Add(s, &s->btns[2], 400, ClassicGenScreen_Huge); + ButtonWidget_Add(s, &s->cancel, 400, Menu_SwitchPause); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -1375,11 +1339,7 @@ static struct SaveLevelScreen { struct TextWidget desc; } SaveLevelScreen; -static struct Widget* save_widgets[] = { - (struct Widget*)&SaveLevelScreen.save, (struct Widget*)&SaveLevelScreen.file, - (struct Widget*)&SaveLevelScreen.cancel, - (struct Widget*)&SaveLevelScreen.input, (struct Widget*)&SaveLevelScreen.desc, -}; +static struct Widget* save_widgets[3 + 1 + 1]; static void SaveLevelScreen_UpdateSave(struct SaveLevelScreen* s) { ButtonWidget_SetConst(&s->save, @@ -1533,7 +1493,7 @@ static void SaveLevelScreen_ContextRecreated(void* screen) { #endif } -static void SaveLevelScreen_Update(void* screen, double delta) { +static void SaveLevelScreen_Update(void* screen, float delta) { struct SaveLevelScreen* s = (struct SaveLevelScreen*)screen; s->input.base.caretAccumulator += delta; } @@ -1553,15 +1513,16 @@ static void SaveLevelScreen_Init(void* screen) { struct MenuInputDesc desc; s->widgets = save_widgets; - s->numWidgets = Array_Elems(save_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(save_widgets); MenuInput_Path(desc); - ButtonWidget_Init(&s->save, 400, SaveLevelScreen_Save); - ButtonWidget_Init(&s->file, 400, SaveLevelScreen_File); + ButtonWidget_Add(s, &s->save, 400, SaveLevelScreen_Save); + ButtonWidget_Add(s, &s->file, 400, SaveLevelScreen_File); - ButtonWidget_Init(&s->cancel, 400, Menu_SwitchPause); - TextInputWidget_Create(&s->input, 400, &World.Name, &desc); - TextWidget_Init(&s->desc); + ButtonWidget_Add(s, &s->cancel, 400, Menu_SwitchPause); + TextInputWidget_Add(s, &s->input, 400, &World.Name, &desc); + TextWidget_Add(s, &s->desc); s->input.onscreenPlaceholder = "Map name"; s->maxVertices = Screen_CalcDefaultMaxVertices(s); @@ -1629,7 +1590,7 @@ static void TexturePackScreen_UploadCallback(const cc_string* path) { TexturePack_ExtractCurrent(true); } -static void TexturePackScreen_UploadFunc(void* s, void* w) { +static void TexturePackScreen_ActionFunc(void* s, void* w) { static const char* const filters[] = { ".zip", NULL }; @@ -1646,7 +1607,13 @@ static void TexturePackScreen_UploadFunc(void* s, void* w) { void TexturePackScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Select a texture pack"; - s->UploadClick = TexturePackScreen_UploadFunc; +#ifdef CC_BUILD_WEB + s->actionText = "Upload"; +#else + s->actionText = "Load file..."; +#endif + + s->ActionClick = TexturePackScreen_ActionFunc; s->LoadEntries = TexturePackScreen_LoadEntries; s->EntryClick = TexturePackScreen_EntryClick; s->DoneClick = Menu_SwitchPause; @@ -1689,10 +1656,40 @@ static void FontListScreen_LoadEntries(struct ListScreen* s) { ListScreen_Select(s, SysFonts_UNSAFE_GetDefault()); } +static void FontListScreen_RegisterCallback(const cc_string* path) { + Chat_Add1("Loaded font from %s", path); +} + +static void FontListScreen_UploadCallback(const cc_string* path) { + cc_result res = SysFonts_Register(path, FontListScreen_RegisterCallback); + + if (res) { + Logger_SimpleWarn2(res, "loading font from", path); + } else { + SysFonts_SaveCache(); + } +} + +static void FontListScreen_ActionFunc(void* s, void* w) { + static const char* const filters[] = { + ".ttf", ".otf", NULL + }; + static struct OpenFileDialogArgs args = { + "Font files", filters, + FontListScreen_UploadCallback, + OFD_UPLOAD_DELETE, "tmp" + }; + + cc_result res = Window_OpenFileDialog(&args); + if (res) Logger_SimpleWarn(res, "showing open file dialog"); +} + void FontListScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Select a font"; - s->UploadClick = NULL; + s->actionText = "Load font..."; + s->ActionClick = FontListScreen_ActionFunc; + s->LoadEntries = FontListScreen_LoadEntries; s->EntryClick = FontListScreen_EntryClick; s->DoneClick = Menu_SwitchGui; @@ -1713,9 +1710,6 @@ static void HotkeyListScreen_EntryClick(void* screen, void* widget) { int i, mods = 0; text = ListScreen_UNSAFE_GetCur(s, widget); - if (!text.length) { - EditHotkeyScreen_Show(original); return; - } String_UNSAFE_Separate(&text, '+', &key, &value); if (String_ContainsConst(&value, "Ctrl")) mods |= HOTKEY_MOD_CTRL; @@ -1754,24 +1748,24 @@ static void HotkeyListScreen_LoadEntries(struct ListScreen* s) { } StringsBuffer_Add(&s->entries, &text); } - - /* Placeholder for 'add new hotkey' */ - StringsBuffer_Add(&s->entries, &String_Empty); StringsBuffer_Sort(&s->entries); } static void HotkeyListScreen_UpdateEntry(struct ListScreen* s, struct ButtonWidget* button, const cc_string* text) { - if (text->length) { - ButtonWidget_Set(button, text, &s->font); - } else { - ButtonWidget_SetConst(button, "New hotkey...", &s->font); - } + if (text->length) ButtonWidget_Set(button, text, &s->font); +} + +static void HotkeyListScreen_ActionFunc(void* s, void* w) { + struct HotkeyData original = { 0 }; + EditHotkeyScreen_Show(original); } void HotkeyListScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Modify hotkeys"; - s->UploadClick = NULL; + s->actionText = "New hotkey..."; + + s->ActionClick = HotkeyListScreen_ActionFunc; s->LoadEntries = HotkeyListScreen_LoadEntries; s->EntryClick = HotkeyListScreen_EntryClick; s->DoneClick = Menu_SwitchPause; @@ -1815,7 +1809,7 @@ static void LoadLevelScreen_LoadEntries(struct ListScreen* s) { } static void LoadLevelScreen_UploadCallback(const cc_string* path) { Map_LoadFrom(path); } -static void LoadLevelScreen_UploadFunc(void* s, void* w) { +static void LoadLevelScreen_ActionFunc(void* s, void* w) { static const char* const filters[] = { ".cw", ".dat", ".lvl", ".mine", ".fcm", ".mclevel", NULL }; /* TODO not hardcode list */ @@ -1832,7 +1826,13 @@ static void LoadLevelScreen_UploadFunc(void* s, void* w) { void LoadLevelScreen_Show(void) { struct ListScreen* s = &ListScreen; s->titleText = "Load level"; - s->UploadClick = LoadLevelScreen_UploadFunc; +#ifdef CC_BUILD_WEB + s->actionText = "Upload"; +#else + s->actionText = "Load file..."; +#endif + + s->ActionClick = LoadLevelScreen_ActionFunc; s->LoadEntries = LoadLevelScreen_LoadEntries; s->EntryClick = LoadLevelScreen_EntryClick; s->DoneClick = Menu_SwitchPause; @@ -1850,10 +1850,7 @@ static struct BindsSourceScreen { } BindsSourceScreen; static int binds_gamepad; /* Default to Normal (Keyboard/Mouse) */ -static struct Widget* bindsSource_widgets[] = { - (struct Widget*)&BindsSourceScreen.btns[0], (struct Widget*)&BindsSourceScreen.btns[1], - (struct Widget*)&BindsSourceScreen.cancel -}; +static struct Widget* bindsSource_widgets[3]; static void BindsSourceScreen_ModeNormal(void* screen, void* b) { binds_gamepad = false; @@ -1888,12 +1885,13 @@ static void BindsSourceScreen_Init(void* screen) { struct BindsSourceScreen* s = (struct BindsSourceScreen*)screen; s->widgets = bindsSource_widgets; - s->numWidgets = Array_Elems(bindsSource_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(bindsSource_widgets); s->selectedI = -1; - ButtonWidget_Init(&s->btns[0], 300, BindsSourceScreen_ModeNormal); - ButtonWidget_Init(&s->btns[1], 300, BindsSourceScreen_ModeGamepad); - ButtonWidget_Init(&s->cancel, 400, Menu_SwitchPause); + ButtonWidget_Add(s, &s->btns[0], 300, BindsSourceScreen_ModeNormal); + ButtonWidget_Add(s, &s->btns[1], 300, BindsSourceScreen_ModeGamepad); + ButtonWidget_Add(s, &s->cancel, 400, Menu_SwitchPause); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -1949,12 +1947,7 @@ static struct KeyBindsScreen { struct ButtonWidget buttons[KEYBINDS_MAX_BTNS]; } KeyBindsScreen; -static struct Widget* key_widgets[KEYBINDS_MAX_BTNS + 5] = { - NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL, - (struct Widget*)&KeyBindsScreen.title, (struct Widget*)&KeyBindsScreen.msg, - (struct Widget*)&KeyBindsScreen.back, (struct Widget*)&KeyBindsScreen.left, - (struct Widget*)&KeyBindsScreen.right -}; +static struct Widget* key_widgets[KEYBINDS_MAX_BTNS + 5]; static void KeyBindsScreen_Update(struct KeyBindsScreen* s, int i) { cc_string text; char textBuffer[STRING_SIZE]; @@ -1971,8 +1964,10 @@ static void KeyBindsScreen_Update(struct KeyBindsScreen* s, int i) { static void KeyBindsScreen_OnBindingClick(void* screen, void* widget) { struct KeyBindsScreen* s = (struct KeyBindsScreen*)screen; + struct ButtonWidget* btn = (struct ButtonWidget*)widget; + int old = s->curI; - s->curI = Screen_Index(s, widget); + s->curI = btn->meta.val; s->closable = false; KeyBindsScreen_Update(s, s->curI); @@ -2055,26 +2050,27 @@ static void KeyBindsScreen_Init(void* screen) { struct KeyBindsScreen* s = (struct KeyBindsScreen*)screen; int i; s->widgets = key_widgets; - s->numWidgets = KEYBINDS_MAX_BTNS + 3; + s->numWidgets = 0; + s->maxWidgets = Array_Elems(key_widgets); s->curI = -1; - for (i = 0; i < s->bindsCount; i++) { - ButtonWidget_Init(&s->buttons[i], s->btnWidth, KeyBindsScreen_OnBindingClick); + for (i = 0; i < s->bindsCount; i++) + { + ButtonWidget_Add(s, &s->buttons[i], s->btnWidth, KeyBindsScreen_OnBindingClick); s->widgets[i] = (struct Widget*)&s->buttons[i]; + s->buttons[i].meta.val = i; } - for (; i < KEYBINDS_MAX_BTNS; i++) { s->widgets[i] = NULL; } - TextWidget_Init(&s->title); - TextWidget_Init(&s->msg); - ButtonWidget_Init(&s->back, 400, Gui.ClassicMenu ? Menu_SwitchClassicOptions : Menu_SwitchOptions); + TextWidget_Add(s, &s->title); + TextWidget_Add(s, &s->msg); + ButtonWidget_Add(s, &s->back, 400, Gui.ClassicMenu ? Menu_SwitchClassicOptions : Menu_SwitchOptions); - ButtonWidget_Init(&s->left, 40, s->leftPage); - ButtonWidget_Init(&s->right, 40, s->rightPage); - Widget_SetDisabled(&s->left, !s->leftPage); - Widget_SetDisabled(&s->right, !s->rightPage); - - if (s->leftPage || s->rightPage) - s->numWidgets += 2; + if (s->leftPage || s->rightPage) { + ButtonWidget_Add(s, &s->left, 40, s->leftPage); + ButtonWidget_Add(s, &s->right, 40, s->rightPage); + Widget_SetDisabled(&s->left, !s->leftPage); + Widget_SetDisabled(&s->right, !s->rightPage); + } s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -2218,7 +2214,6 @@ void HotbarBindingsScreen_Show(void) { /*########################################################################################################################* *--------------------------------------------------MenuInputOverlay-------------------------------------------------------* *#########################################################################################################################*/ -typedef void (*MenuInputDone)(const cc_string* value, cc_bool valid); static struct MenuInputOverlay { Screen_Body cc_bool screenMode; @@ -2230,10 +2225,7 @@ static struct MenuInputOverlay { cc_string value; char valueBuffer[STRING_SIZE]; } MenuInputOverlay; -static struct Widget* menuInput_widgets[] = { - (struct Widget*)&MenuInputOverlay.ok, (struct Widget*)&MenuInputOverlay.Default, - (struct Widget*)&MenuInputOverlay.input -}; +static struct Widget* menuInput_widgets[2 + 1]; static void MenuInputOverlay_Close(struct MenuInputOverlay* s, cc_bool valid) { Gui_Remove((struct Screen*)&MenuInputOverlay); @@ -2294,11 +2286,12 @@ static void MenuInputOverlay_Default(void* screen, void* widget) { static void MenuInputOverlay_Init(void* screen) { struct MenuInputOverlay* s = (struct MenuInputOverlay*)screen; s->widgets = menuInput_widgets; - s->numWidgets = Array_Elems(menuInput_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(menuInput_widgets); - TextInputWidget_Create(&s->input, 400, &s->value, s->desc); - ButtonWidget_Init(&s->Default, 200, MenuInputOverlay_Default); - ButtonWidget_Init(&s->ok, Input_TouchMode ? 200 : 40, MenuInputOverlay_OK); + ButtonWidget_Add(s, &s->ok, Input_TouchMode ? 200 : 40, MenuInputOverlay_OK); + ButtonWidget_Add(s, &s->Default, 200, MenuInputOverlay_Default); + TextInputWidget_Add(s, &s->input, 400, &s->value, s->desc); if (s->desc->VTABLE == &IntInput_VTABLE) { s->input.onscreenType = KEYBOARD_TYPE_INTEGER; @@ -2309,12 +2302,12 @@ static void MenuInputOverlay_Init(void* screen) { s->maxVertices = Screen_CalcDefaultMaxVertices(s); } -static void MenuInputOverlay_Update(void* screen, double delta) { +static void MenuInputOverlay_Update(void* screen, float delta) { struct MenuInputOverlay* s = (struct MenuInputOverlay*)screen; s->input.base.caretAccumulator += delta; } -static void MenuInputOverlay_Render(void* screen, double delta) { +static void MenuInputOverlay_Render(void* screen, float delta) { struct MenuInputOverlay* s = (struct MenuInputOverlay*)screen; if (s->screenMode) Menu_RenderBounds(); @@ -2322,11 +2315,7 @@ static void MenuInputOverlay_Render(void* screen, double delta) { } static void MenuInputOverlay_Free(void* screen) { - struct MenuInputOverlay* s = (struct MenuInputOverlay*)screen; - Elem_Free(&s->input.base); - Elem_Free(&s->ok); - Elem_Free(&s->Default); - Window_CloseKeyboard(); + OnscreenKeyboard_Close(); } static void MenuInputOverlay_Layout(void* screen) { @@ -2397,11 +2386,10 @@ static void MenuOptionsScreen_Layout(void* screen); static struct MenuOptionsScreen { Screen_Body - struct MenuInputDesc* descs; const char* descriptions[MENUOPTS_MAX_OPTS + 1]; - int activeI; + struct ButtonWidget* activeBtn; InitMenuOptions DoInit, DoRecreateExtra, OnHacksChanged; - int numButtons, numCore; + int numButtons; struct FontDesc titleFont, textFont; struct TextGroupWidget extHelp; struct Texture extHelpTextures[5]; /* max lines is 5 */ @@ -2409,11 +2397,8 @@ static struct MenuOptionsScreen { const char* extHelpDesc; } MenuOptionsScreen_Instance; -static struct Widget* menuOpts_widgets[MENUOPTS_MAX_OPTS + 1] = { - NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL, - NULL, - (struct Widget*)&MenuOptionsScreen_Instance.done -}; +static struct MenuInputDesc menuOpts_descs[MENUOPTS_MAX_OPTS]; +static struct Widget* menuOpts_widgets[MENUOPTS_MAX_OPTS + 1]; static void Menu_GetBool(cc_string* raw, cc_bool v) { String_AppendConst(raw, v ? "ON" : "OFF"); @@ -2433,21 +2418,22 @@ static void MenuOptionsScreen_SetFPS(const cc_string* v) { Game_SetFpsLimit(method); } -static void MenuOptionsScreen_Update(struct MenuOptionsScreen* s, int i) { +static void MenuOptionsScreen_Update(struct MenuOptionsScreen* s, struct ButtonWidget* btn) { cc_string title; char titleBuffer[STRING_SIZE]; String_InitArray(title, titleBuffer); - String_AppendConst(&title, s->buttons[i].optName); - if (s->buttons[i].GetValue) { + String_AppendConst(&title, btn->optName); + if (btn->GetValue) { String_AppendConst(&title, ": "); - s->buttons[i].GetValue(&title); + btn->GetValue(&title); } - ButtonWidget_Set(&s->buttons[i], &title, &s->titleFont); + ButtonWidget_Set(btn, &title, &s->titleFont); } -CC_NOINLINE static void MenuOptionsScreen_Set(struct MenuOptionsScreen* s, int i, const cc_string* text) { - s->buttons[i].SetValue(text); - MenuOptionsScreen_Update(s, i); +CC_NOINLINE static void MenuOptionsScreen_Set(struct MenuOptionsScreen* s, + struct ButtonWidget* btn, const cc_string* text) { + btn->SetValue(text); + MenuOptionsScreen_Update(s, btn); } CC_NOINLINE static void MenuOptionsScreen_FreeExtHelp(struct MenuOptionsScreen* s) { @@ -2477,7 +2463,7 @@ static void MenuOptionsScreen_SelectExtHelp(struct MenuOptionsScreen* s, int idx cc_string descRaw, descLines[5]; MenuOptionsScreen_FreeExtHelp(s); - if (s->activeI >= 0) return; + if (s->activeBtn) return; desc = s->descriptions[idx]; if (!desc) return; @@ -2495,14 +2481,14 @@ static void MenuOptionsScreen_SelectExtHelp(struct MenuOptionsScreen* s, int idx static void MenuOptionsScreen_OnDone(const cc_string* value, cc_bool valid) { struct MenuOptionsScreen* s = &MenuOptionsScreen_Instance; if (valid) { - MenuOptionsScreen_Set(s, s->activeI, value); + MenuOptionsScreen_Set(s, s->activeBtn, value); /* Marking screen as dirty fixes changed option widget appearing wrong */ /* for a few frames (e.g. Chatlines options changed from '12' to '1') */ s->dirty = true; } - MenuOptionsScreen_SelectExtHelp(s, s->activeI); - s->activeI = -1; + if (s->selectedI >= 0) MenuOptionsScreen_SelectExtHelp(s, s->selectedI); + s->activeBtn = NULL; } static int MenuOptionsScreen_PointerMove(void* screen, int id, int x, int y) { @@ -2511,26 +2497,27 @@ static int MenuOptionsScreen_PointerMove(void* screen, int id, int x, int y) { if (i == -1 || i == s->selectedI) return true; s->selectedI = i; - if (s->activeI == -1) MenuOptionsScreen_SelectExtHelp(s, i); + if (!s->activeBtn) MenuOptionsScreen_SelectExtHelp(s, i); return true; } -static void MenuOptionsScreen_InitButtons(struct MenuOptionsScreen* s, const struct MenuOptionDesc* btns, int count, Widget_LeftClick backClick) { +static void MenuOptionsScreen_AddButtons(struct MenuOptionsScreen* s, const struct MenuOptionDesc* btns, int count, Widget_LeftClick backClick) { struct ButtonWidget* btn; int i; for (i = 0; i < count; i++) { btn = &s->buttons[i]; - ButtonWidget_Make(btn, 300, btns[i].OnClick, - ANCHOR_CENTRE, ANCHOR_CENTRE, btns[i].dir * 160, btns[i].y); + ButtonWidget_Add(s, btn, 300, btns[i].OnClick); + Widget_SetLocation(btn, ANCHOR_CENTRE, ANCHOR_CENTRE, btns[i].dir * 160, btns[i].y); btn->optName = btns[i].name; btn->GetValue = btns[i].GetValue; btn->SetValue = btns[i].SetValue; + btn->meta.ptr = &menuOpts_descs[i]; s->widgets[i] = (struct Widget*)btn; } s->numButtons = count; - ButtonWidget_Init(&s->done, 400, backClick); + ButtonWidget_Add(s, &s->done, 400, backClick); } static void MenuOptionsScreen_Bool(void* screen, void* widget) { @@ -2544,35 +2531,33 @@ static void MenuOptionsScreen_Bool(void* screen, void* widget) { isOn = String_CaselessEqualsConst(&value, "ON"); value = String_FromReadonly(isOn ? "OFF" : "ON"); - MenuOptionsScreen_Set(s, Screen_Index(s, btn), &value); + MenuOptionsScreen_Set(s, btn, &value); } static void MenuOptionsScreen_Enum(void* screen, void* widget) { cc_string value; char valueBuffer[STRING_SIZE]; struct MenuOptionsScreen* s = (struct MenuOptionsScreen*)screen; struct ButtonWidget* btn = (struct ButtonWidget*)widget; - int index; struct MenuInputDesc* desc; const char* const* names; int raw, count; - index = Screen_Index(s, btn); String_InitArray(value, valueBuffer); btn->GetValue(&value); - desc = &s->descs[index]; + desc = (struct MenuInputDesc*)btn->meta.ptr; names = desc->meta.e.Names; count = desc->meta.e.Count; raw = (Utils_ParseEnum(&value, 0, names, count) + 1) % count; value = String_FromReadonly(names[raw]); - MenuOptionsScreen_Set(s, index, &value); + MenuOptionsScreen_Set(s, btn, &value); } static void MenuInputOverlay_CheckStillValid(struct MenuOptionsScreen* s) { - if (s->activeI == -1) return; + if (!s->activeBtn) return; - if (s->widgets[s->activeI]->flags & WIDGET_FLAG_DISABLED) { + if (s->activeBtn->flags & WIDGET_FLAG_DISABLED) { /* source button is disabled now, so close open input overlay */ MenuInputOverlay_Close(&MenuInputOverlay, false); } @@ -2585,12 +2570,12 @@ static void MenuOptionsScreen_Input(void* screen, void* widget) { struct MenuInputDesc* desc; MenuOptionsScreen_FreeExtHelp(s); - s->activeI = Screen_Index(s, btn); + s->activeBtn = btn; String_InitArray(value, valueBuffer); btn->GetValue(&value); - desc = &s->descs[s->activeI]; - MenuInputOverlay_Show(desc, &value, MenuOptionsScreen_OnDone, Input_TouchMode); + desc = (struct MenuInputDesc*)btn->meta.ptr; + MenuInputOverlay_Show(desc, &value, MenuOptionsScreen_OnDone, Gui.TouchUI); } static void MenuOptionsScreen_OnHacksChanged(void* screen) { @@ -2603,9 +2588,9 @@ static void MenuOptionsScreen_Init(void* screen) { struct MenuOptionsScreen* s = (struct MenuOptionsScreen*)screen; int i; - s->widgets = menuOpts_widgets; - s->numWidgets = MENUOPTS_MAX_OPTS + 1; /* always have back button */ - s->maxVertices = BUTTONWIDGET_MAX; + s->widgets = menuOpts_widgets; + s->numWidgets = 0; + s->maxWidgets = MENUOPTS_MAX_OPTS + 1; /* always have back button */ /* The various menu options screens might have different number of widgets */ for (i = 0; i < MENUOPTS_MAX_OPTS; i++) { @@ -2613,17 +2598,19 @@ static void MenuOptionsScreen_Init(void* screen) { s->descriptions[i] = NULL; } - s->activeI = -1; + s->activeBtn = NULL; s->selectedI = -1; s->DoInit(s); TextGroupWidget_Create(&s->extHelp, 5, s->extHelpTextures, MenuOptionsScreen_GetDesc); s->extHelp.lines = 0; Event_Register_(&UserEvents.HackPermsChanged, screen, MenuOptionsScreen_OnHacksChanged); + + s->maxVertices = Screen_CalcDefaultMaxVertices(s); } #define EXTHELP_PAD 5 /* padding around extended help box */ -static void MenuOptionsScreen_Render(void* screen, double delta) { +static void MenuOptionsScreen_Render(void* screen, float delta) { struct MenuOptionsScreen* s = (struct MenuOptionsScreen*)screen; struct TextGroupWidget* w; PackedCol tableColor = PackedCol_Make(20, 20, 20, 200); @@ -2666,8 +2653,9 @@ static void MenuOptionsScreen_ContextRecreated(void* screen) { Gui_MakeBodyFont(&s->textFont); Screen_UpdateVb(screen); - for (i = 0; i < s->numButtons; i++) { - if (s->widgets[i]) MenuOptionsScreen_Update(s, i); + for (i = 0; i < s->numButtons; i++) + { + if (s->widgets[i]) MenuOptionsScreen_Update(s, &s->buttons[i]); } ButtonWidget_SetConst(&s->done, "Done", &s->titleFont); @@ -2683,13 +2671,12 @@ static const struct ScreenVTABLE MenuOptionsScreen_VTABLE = { Menu_PointerDown, Screen_PointerUp, MenuOptionsScreen_PointerMove, Screen_TMouseScroll, MenuOptionsScreen_Layout, MenuOptionsScreen_ContextLost, MenuOptionsScreen_ContextRecreated }; -void MenuOptionsScreen_Show(struct MenuInputDesc* descs, InitMenuOptions init) { +void MenuOptionsScreen_Show(InitMenuOptions init) { struct MenuOptionsScreen* s = &MenuOptionsScreen_Instance; s->grabsInput = true; s->closable = true; s->VTABLE = &MenuOptionsScreen_VTABLE; - s->descs = descs; s->DoInit = init; s->DoRecreateExtra = NULL; s->OnHacksChanged = NULL; @@ -2729,9 +2716,9 @@ static void ClassicOptionsScreen_SetViewDist(const cc_string* v) { Game_UserSetViewDistance(dist); } -static void ClassicOptionsScreen_GetPhysics(cc_string* v) { Menu_GetBool(v, Physics.Enabled); } -static void ClassicOptionsScreen_SetPhysics(const cc_string* v) { - Physics_SetEnabled(Menu_SetBool(v, OPT_BLOCK_PHYSICS)); +static void ClassicOptionsScreen_GetAnaglyph(cc_string* v) { Menu_GetBool(v, Game_Anaglyph3D); } +static void ClassicOptionsScreen_SetAnaglyph(const cc_string* v) { + Game_Anaglyph3D = Menu_SetBool(v, OPT_ANAGLYPH3D); } static void ClassicOptionsScreen_GetSounds(cc_string* v) { Menu_GetBool(v, Audio_SoundsVolume > 0); } @@ -2746,10 +2733,10 @@ static void ClassicOptionsScreen_SetShowFPS(const cc_string* v) { Gui.ShowFPS = static void ClassicOptionsScreen_GetViewBob(cc_string* v) { Menu_GetBool(v, Game_ViewBobbing); } static void ClassicOptionsScreen_SetViewBob(const cc_string* v) { Game_ViewBobbing = Menu_SetBool(v, OPT_VIEW_BOBBING); } -static void ClassicOptionsScreen_GetHacks(cc_string* v) { Menu_GetBool(v, LocalPlayer_Instance.Hacks.Enabled); } +static void ClassicOptionsScreen_GetHacks(cc_string* v) { Menu_GetBool(v, Entities.CurPlayer->Hacks.Enabled); } static void ClassicOptionsScreen_SetHacks(const cc_string* v) { - LocalPlayer_Instance.Hacks.Enabled = Menu_SetBool(v, OPT_HACKS_ENABLED); - HacksComp_Update(&LocalPlayer_Instance.Hacks); + Entities.CurPlayer->Hacks.Enabled = Menu_SetBool(v, OPT_HACKS_ENABLED); + HacksComp_Update(&Entities.CurPlayer->Hacks); } static void ClassicOptionsScreen_RecreateExtra(struct MenuOptionsScreen* s) { @@ -2764,8 +2751,8 @@ static void ClassicOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { ClassicOptionsScreen_GetInvert, ClassicOptionsScreen_SetInvert }, { -1, -50, "Render distance", MenuOptionsScreen_Enum, ClassicOptionsScreen_GetViewDist, ClassicOptionsScreen_SetViewDist }, - { -1, 0, "Block physics", MenuOptionsScreen_Bool, - ClassicOptionsScreen_GetPhysics, ClassicOptionsScreen_SetPhysics }, + { -1, 0, "3D anaglyph", MenuOptionsScreen_Bool, + ClassicOptionsScreen_GetAnaglyph, ClassicOptionsScreen_SetAnaglyph }, { 1, -150, "Sound", MenuOptionsScreen_Bool, ClassicOptionsScreen_GetSounds, ClassicOptionsScreen_SetSounds }, @@ -2778,14 +2765,11 @@ static void ClassicOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { { 0, 60, "Hacks enabled", MenuOptionsScreen_Bool, ClassicOptionsScreen_GetHacks, ClassicOptionsScreen_SetHacks } }; - s->numCore = 9 + 1; - s->maxVertices += 9 * BUTTONWIDGET_MAX + BUTTONWIDGET_MAX; s->DoRecreateExtra = ClassicOptionsScreen_RecreateExtra; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchPause); - ButtonWidget_Make(&s->buttons[9], 400, Menu_SwitchBindsClassic, - ANCHOR_CENTRE, ANCHOR_MAX, 0, 95); - s->widgets[9] = (struct Widget*)&s->buttons[9]; + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchPause); + ButtonWidget_Add(s, &s->buttons[9], 400, Menu_SwitchBindsClassic); + Widget_SetLocation(&s->buttons[9], ANCHOR_CENTRE, ANCHOR_MAX, 0, 95); /* Disable certain options */ if (!Server.IsSinglePlayer) Menu_Remove(s, 3); @@ -2793,11 +2777,10 @@ static void ClassicOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { } void ClassicOptionsScreen_Show(void) { - static struct MenuInputDesc descs[11]; - MenuInput_Enum(descs[2], viewDistNames, VIEW_COUNT); - MenuInput_Enum(descs[7], FpsLimit_Names, FPS_LIMIT_COUNT); + MenuInput_Enum(menuOpts_descs[2], viewDistNames, VIEW_COUNT); + MenuInput_Enum(menuOpts_descs[7], FpsLimit_Names, FPS_LIMIT_COUNT); - MenuOptionsScreen_Show(descs, ClassicOptionsScreen_InitWidgets); + MenuOptionsScreen_Show(ClassicOptionsScreen_InitWidgets); } @@ -2861,27 +2844,23 @@ static void EnvSettingsScreen_InitWidgets(struct MenuOptionsScreen* s) { { 1, 50, "Water level", MenuOptionsScreen_Input, EnvSettingsScreen_GetEdgeHeight, EnvSettingsScreen_SetEdgeHeight } }; - - s->numCore = 10; - s->maxVertices += 10 * BUTTONWIDGET_MAX; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); } void EnvSettingsScreen_Show(void) { - static struct MenuInputDesc descs[11]; - MenuInput_Hex(descs[0], ENV_DEFAULT_CLOUDS_COLOR); - MenuInput_Hex(descs[1], ENV_DEFAULT_SKY_COLOR); - MenuInput_Hex(descs[2], ENV_DEFAULT_FOG_COLOR); - MenuInput_Float(descs[3], 0, 1000, 1); - MenuInput_Int(descs[4], -10000, 10000, World.Height + 2); + MenuInput_Hex(menuOpts_descs[0], ENV_DEFAULT_CLOUDS_COLOR); + MenuInput_Hex(menuOpts_descs[1], ENV_DEFAULT_SKY_COLOR); + MenuInput_Hex(menuOpts_descs[2], ENV_DEFAULT_FOG_COLOR); + MenuInput_Float(menuOpts_descs[3], 0, 1000, 1); + MenuInput_Int(menuOpts_descs[4], -10000, 10000, World.Height + 2); - MenuInput_Hex(descs[5], ENV_DEFAULT_SUN_COLOR); - MenuInput_Hex(descs[6], ENV_DEFAULT_SHADOW_COLOR); - MenuInput_Enum(descs[7], Weather_Names, Array_Elems(Weather_Names)); - MenuInput_Float(descs[8], -100, 100, 1); - MenuInput_Int(descs[9], -2048, 2048, World.Height / 2); + MenuInput_Hex(menuOpts_descs[5], ENV_DEFAULT_SUN_COLOR); + MenuInput_Hex(menuOpts_descs[6], ENV_DEFAULT_SHADOW_COLOR); + MenuInput_Enum(menuOpts_descs[7], Weather_Names, Array_Elems(Weather_Names)); + MenuInput_Float(menuOpts_descs[8], -100, 100, 1); + MenuInput_Int(menuOpts_descs[9], -2048, 2048, World.Height / 2); - MenuOptionsScreen_Show(descs, EnvSettingsScreen_InitWidgets); + MenuOptionsScreen_Show(EnvSettingsScreen_InitWidgets); } @@ -2936,33 +2915,32 @@ static void GraphicsOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { static const struct MenuOptionDesc buttons[] = { { -1, -150, "Camera Mass", MenuOptionsScreen_Input, GraphicsOptionsScreen_GetCameraMass, GraphicsOptionsScreen_SetCameraMass }, - { -1, -100, "FPS mode", MenuOptionsScreen_Enum, + { -1, -100, "FPS mode", MenuOptionsScreen_Enum, MenuOptionsScreen_GetFPS, MenuOptionsScreen_SetFPS }, - { -1, -50, "View distance", MenuOptionsScreen_Input, + { -1, -50, "View distance", MenuOptionsScreen_Input, GraphicsOptionsScreen_GetViewDist, GraphicsOptionsScreen_SetViewDist }, - { -1, 0, "Smooth lighting", MenuOptionsScreen_Bool, + { -1, 0, "Advanced lighting", MenuOptionsScreen_Bool, GraphicsOptionsScreen_GetSmooth, GraphicsOptionsScreen_SetSmooth }, { -1, 50, "Modern lighting", MenuOptionsScreen_Bool, GraphicsOptionsScreen_GetModernLighting, GraphicsOptionsScreen_SetModernLighting }, { 1, -150, "Smooth camera", MenuOptionsScreen_Bool, GraphicsOptionsScreen_GetCamera, GraphicsOptionsScreen_SetCamera }, - { 1, -100, "Names", MenuOptionsScreen_Enum, + { 1, -100, "Names", MenuOptionsScreen_Enum, GraphicsOptionsScreen_GetNames, GraphicsOptionsScreen_SetNames }, - { 1, -50, "Shadows", MenuOptionsScreen_Enum, + { 1, -50, "Shadows", MenuOptionsScreen_Enum, GraphicsOptionsScreen_GetShadows, GraphicsOptionsScreen_SetShadows }, #ifdef CC_BUILD_N64 - { 1, 50, "Filtering", MenuOptionsScreen_Bool, - GraphicsOptionsScreen_GetMipmaps, GraphicsOptionsScreen_SetMipmaps } + { 1, 0, "Filtering", MenuOptionsScreen_Bool, + GraphicsOptionsScreen_GetMipmaps, GraphicsOptionsScreen_SetMipmaps }, #else - { 1, 50, "Mipmaps", MenuOptionsScreen_Bool, - GraphicsOptionsScreen_GetMipmaps, GraphicsOptionsScreen_SetMipmaps } + { 1, 0, "Mipmaps", MenuOptionsScreen_Bool, + GraphicsOptionsScreen_GetMipmaps, GraphicsOptionsScreen_SetMipmaps }, #endif + { 1, 50, "Anaglyph 3D", MenuOptionsScreen_Bool, + ClassicOptionsScreen_GetAnaglyph, ClassicOptionsScreen_SetAnaglyph } }; - - s->numCore = 9; - s->maxVertices += 9 * BUTTONWIDGET_MAX; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); s->descriptions[0] = "&eChange the smoothness of the smooth camera."; s->descriptions[1] = \ @@ -2986,14 +2964,13 @@ static void GraphicsOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { } void GraphicsOptionsScreen_Show(void) { - static struct MenuInputDesc descs[9]; - MenuInput_Float(descs[0], 1, 100, 20); - MenuInput_Enum(descs[1], FpsLimit_Names, FPS_LIMIT_COUNT); - MenuInput_Int(descs[2], 8, 4096, 512); - MenuInput_Enum(descs[6], NameMode_Names, NAME_MODE_COUNT); - MenuInput_Enum(descs[7], ShadowMode_Names, SHADOW_MODE_COUNT); + MenuInput_Float(menuOpts_descs[0], 1, 100, 20); + MenuInput_Enum(menuOpts_descs[1], FpsLimit_Names, FPS_LIMIT_COUNT); + MenuInput_Int(menuOpts_descs[2], 8, 4096, 512); + MenuInput_Enum(menuOpts_descs[5], NameMode_Names, NAME_MODE_COUNT); + MenuInput_Enum(menuOpts_descs[6], ShadowMode_Names, SHADOW_MODE_COUNT); - MenuOptionsScreen_Show(descs, GraphicsOptionsScreen_InitWidgets); + MenuOptionsScreen_Show(GraphicsOptionsScreen_InitWidgets); } @@ -3006,6 +2983,12 @@ static void ChatOptionsScreen_SetScale(const cc_string* v, float* target, const Gui_LayoutAll(); } +static void ChatOptionsScreen_GetAutoScaleChat(cc_string* v) { Menu_GetBool(v, Gui.AutoScaleChat); } +static void ChatOptionsScreen_SetAutoScaleChat(const cc_string* v) { + Gui.AutoScaleChat = Menu_SetBool(v, OPT_CHAT_AUTO_SCALE); + Gui_LayoutAll(); +} + static void ChatOptionsScreen_GetChatScale(cc_string* v) { String_AppendFloat(v, Gui.RawChatScale, 1); } static void ChatOptionsScreen_SetChatScale(const cc_string* v) { ChatOptionsScreen_SetScale(v, &Gui.RawChatScale, OPT_CHAT_SCALE); } @@ -3035,19 +3018,19 @@ static void ChatOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { { 1, 0, "Log to disk", MenuOptionsScreen_Bool, ChatOptionsScreen_GetLogging, ChatOptionsScreen_SetLogging }, { 1, 50, "Clickable chat", MenuOptionsScreen_Bool, - ChatOptionsScreen_GetClickable, ChatOptionsScreen_SetClickable } - }; + ChatOptionsScreen_GetClickable, ChatOptionsScreen_SetClickable }, - s->numCore = 4; - s->maxVertices += 4 * BUTTONWIDGET_MAX; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); + { -1,-50, "Scale with window", MenuOptionsScreen_Bool, + ChatOptionsScreen_GetAutoScaleChat, ChatOptionsScreen_SetAutoScaleChat } + }; + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); } void ChatOptionsScreen_Show(void) { - static struct MenuInputDesc descs[5]; - MenuInput_Float(descs[0], 0.25f, 4.00f, 1); - MenuInput_Int(descs[1], 0, 30, Gui.DefaultLines); - MenuOptionsScreen_Show(descs, ChatOptionsScreen_InitWidgets); + MenuInput_Float(menuOpts_descs[0], 0.25f, 4.00f, 1); + MenuInput_Int(menuOpts_descs[1], 0, 30, Gui.DefaultLines); + + MenuOptionsScreen_Show(ChatOptionsScreen_InitWidgets); } @@ -3069,6 +3052,9 @@ static void GuiOptionsScreen_SetHotbar(const cc_string* v) { ChatOptionsScreen_S static void GuiOptionsScreen_GetInventory(cc_string* v) { String_AppendFloat(v, Gui.RawInventoryScale, 1); } static void GuiOptionsScreen_SetInventory(const cc_string* v) { ChatOptionsScreen_SetScale(v, &Gui.RawInventoryScale, OPT_INVENTORY_SCALE); } +static void GuiOptionsScreen_GetCrosshair(cc_string* v) { String_AppendFloat(v, Gui.RawCrosshairScale, 1); } +static void GuiOptionsScreen_SetCrosshair(const cc_string* v) { ChatOptionsScreen_SetScale(v, &Gui.RawCrosshairScale, OPT_CROSSHAIR_SCALE); } + static void GuiOptionsScreen_GetTabAuto(cc_string* v) { Menu_GetBool(v, Gui.TabAutocomplete); } static void GuiOptionsScreen_SetTabAuto(const cc_string* v) { Gui.TabAutocomplete = Menu_SetBool(v, OPT_TAB_AUTOCOMPLETE); } @@ -3080,15 +3066,18 @@ static void GuiOptionsScreen_SetUseFont(const cc_string* v) { static void GuiOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { static const struct MenuOptionDesc buttons[] = { - { -1, -100, "Black text shadows", MenuOptionsScreen_Bool, - GuiOptionsScreen_GetShadows, GuiOptionsScreen_SetShadows }, - { -1, -50, "Show FPS", MenuOptionsScreen_Bool, + + { -1, -100, "Show FPS", MenuOptionsScreen_Bool, GuiOptionsScreen_GetShowFPS, GuiOptionsScreen_SetShowFPS }, - { -1, 0, "Hotbar scale", MenuOptionsScreen_Input, + { -1, -50, "Hotbar scale", MenuOptionsScreen_Input, GuiOptionsScreen_GetHotbar, GuiOptionsScreen_SetHotbar }, - { -1, 50, "Inventory scale", MenuOptionsScreen_Input, + { -1, 0, "Inventory scale", MenuOptionsScreen_Input, GuiOptionsScreen_GetInventory, GuiOptionsScreen_SetInventory }, + { -1, 50, "Crosshair scale", MenuOptionsScreen_Input, + GuiOptionsScreen_GetCrosshair, GuiOptionsScreen_SetCrosshair }, + { 1, -100, "Black text shadows", MenuOptionsScreen_Bool, + GuiOptionsScreen_GetShadows, GuiOptionsScreen_SetShadows }, { 1, -50, "Tab auto-complete", MenuOptionsScreen_Bool, GuiOptionsScreen_GetTabAuto, GuiOptionsScreen_SetTabAuto }, { 1, 0, "Use system font", MenuOptionsScreen_Bool, @@ -3096,32 +3085,30 @@ static void GuiOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { { 1, 50, "Select system font", Menu_SwitchFont, NULL, NULL } }; - - s->numCore = 7; - s->maxVertices += 7 * BUTTONWIDGET_MAX; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); } void GuiOptionsScreen_Show(void) { - static struct MenuInputDesc descs[8]; - MenuInput_Float(descs[2], 0.25f, 4.00f, 1); - MenuInput_Float(descs[3], 0.25f, 4.00f, 1); - MenuOptionsScreen_Show(descs, GuiOptionsScreen_InitWidgets); + MenuInput_Float(menuOpts_descs[1], 0.25f, 4.00f, 1); + MenuInput_Float(menuOpts_descs[2], 0.25f, 4.00f, 1); + MenuInput_Float(menuOpts_descs[3], 0.25f, 4.00f, 1); + + MenuOptionsScreen_Show(GuiOptionsScreen_InitWidgets); } /*########################################################################################################################* *---------------------------------------------------HacksSettingsScreen---------------------------------------------------* *#########################################################################################################################*/ -static void HacksSettingsScreen_GetHacks(cc_string* v) { Menu_GetBool(v, LocalPlayer_Instance.Hacks.Enabled); } +static void HacksSettingsScreen_GetHacks(cc_string* v) { Menu_GetBool(v, Entities.CurPlayer->Hacks.Enabled); } static void HacksSettingsScreen_SetHacks(const cc_string* v) { - LocalPlayer_Instance.Hacks.Enabled = Menu_SetBool(v,OPT_HACKS_ENABLED); - HacksComp_Update(&LocalPlayer_Instance.Hacks); + Entities.CurPlayer->Hacks.Enabled = Menu_SetBool(v,OPT_HACKS_ENABLED); + HacksComp_Update(&Entities.CurPlayer->Hacks); } -static void HacksSettingsScreen_GetSpeed(cc_string* v) { String_AppendFloat(v, LocalPlayer_Instance.Hacks.SpeedMultiplier, 2); } +static void HacksSettingsScreen_GetSpeed(cc_string* v) { String_AppendFloat(v, Entities.CurPlayer->Hacks.SpeedMultiplier, 2); } static void HacksSettingsScreen_SetSpeed(const cc_string* v) { - LocalPlayer_Instance.Hacks.SpeedMultiplier = Menu_Float(v); + Entities.CurPlayer->Hacks.SpeedMultiplier = Menu_Float(v); Options_Set(OPT_SPEED_FACTOR, v); } @@ -3130,12 +3117,15 @@ static void HacksSettingsScreen_SetClipping(const cc_string* v) { Camera.Clipping = Menu_SetBool(v, OPT_CAMERA_CLIPPING); } -static void HacksSettingsScreen_GetJump(cc_string* v) { String_AppendFloat(v, LocalPlayer_JumpHeight(), 3); } +static void HacksSettingsScreen_GetJump(cc_string* v) { + String_AppendFloat(v, LocalPlayer_JumpHeight(Entities.CurPlayer), 3); +} + static void HacksSettingsScreen_SetJump(const cc_string* v) { cc_string str; char strBuffer[STRING_SIZE]; struct PhysicsComp* physics; - physics = &LocalPlayer_Instance.Physics; + physics = &Entities.CurPlayer->Physics; physics->JumpVel = PhysicsComp_CalcJumpVelocity(Menu_Float(v)); physics->UserJumpVel = physics->JumpVel; @@ -3144,19 +3134,19 @@ static void HacksSettingsScreen_SetJump(const cc_string* v) { Options_Set(OPT_JUMP_VELOCITY, &str); } -static void HacksSettingsScreen_GetWOMHacks(cc_string* v) { Menu_GetBool(v, LocalPlayer_Instance.Hacks.WOMStyleHacks); } +static void HacksSettingsScreen_GetWOMHacks(cc_string* v) { Menu_GetBool(v, Entities.CurPlayer->Hacks.WOMStyleHacks); } static void HacksSettingsScreen_SetWOMHacks(const cc_string* v) { - LocalPlayer_Instance.Hacks.WOMStyleHacks = Menu_SetBool(v, OPT_WOM_STYLE_HACKS); + Entities.CurPlayer->Hacks.WOMStyleHacks = Menu_SetBool(v, OPT_WOM_STYLE_HACKS); } -static void HacksSettingsScreen_GetFullStep(cc_string* v) { Menu_GetBool(v, LocalPlayer_Instance.Hacks.FullBlockStep); } +static void HacksSettingsScreen_GetFullStep(cc_string* v) { Menu_GetBool(v, Entities.CurPlayer->Hacks.FullBlockStep); } static void HacksSettingsScreen_SetFullStep(const cc_string* v) { - LocalPlayer_Instance.Hacks.FullBlockStep = Menu_SetBool(v, OPT_FULL_BLOCK_STEP); + Entities.CurPlayer->Hacks.FullBlockStep = Menu_SetBool(v, OPT_FULL_BLOCK_STEP); } -static void HacksSettingsScreen_GetPushback(cc_string* v) { Menu_GetBool(v, LocalPlayer_Instance.Hacks.PushbackPlacing); } +static void HacksSettingsScreen_GetPushback(cc_string* v) { Menu_GetBool(v, Entities.CurPlayer->Hacks.PushbackPlacing); } static void HacksSettingsScreen_SetPushback(const cc_string* v) { - LocalPlayer_Instance.Hacks.PushbackPlacing = Menu_SetBool(v, OPT_PUSHBACK_PLACING); + Entities.CurPlayer->Hacks.PushbackPlacing = Menu_SetBool(v, OPT_PUSHBACK_PLACING); } static void HacksSettingsScreen_GetLiquids(cc_string* v) { Menu_GetBool(v, Game_BreakableLiquids); } @@ -3164,9 +3154,9 @@ static void HacksSettingsScreen_SetLiquids(const cc_string* v) { Game_BreakableLiquids = Menu_SetBool(v, OPT_MODIFIABLE_LIQUIDS); } -static void HacksSettingsScreen_GetSlide(cc_string* v) { Menu_GetBool(v, LocalPlayer_Instance.Hacks.NoclipSlide); } +static void HacksSettingsScreen_GetSlide(cc_string* v) { Menu_GetBool(v, Entities.CurPlayer->Hacks.NoclipSlide); } static void HacksSettingsScreen_SetSlide(const cc_string* v) { - LocalPlayer_Instance.Hacks.NoclipSlide = Menu_SetBool(v, OPT_NOCLIP_SLIDE); + Entities.CurPlayer->Hacks.NoclipSlide = Menu_SetBool(v, OPT_NOCLIP_SLIDE); } static void HacksSettingsScreen_GetFOV(cc_string* v) { String_AppendInt(v, Camera.Fov); } @@ -3181,7 +3171,7 @@ static void HacksSettingsScreen_SetFOV(const cc_string* v) { static void HacksSettingsScreen_CheckHacksAllowed(struct MenuOptionsScreen* s) { struct Widget** widgets = s->widgets; - struct LocalPlayer* p = &LocalPlayer_Instance; + struct LocalPlayer* p = Entities.CurPlayer; cc_bool disabled = !p->Hacks.Enabled; Widget_SetDisabled(widgets[3], disabled || !p->Hacks.CanSpeed); @@ -3215,11 +3205,9 @@ static void HacksSettingsScreen_InitWidgets(struct MenuOptionsScreen* s) { { 1, 50, "Field of view", MenuOptionsScreen_Input, HacksSettingsScreen_GetFOV, HacksSettingsScreen_SetFOV }, }; - s->numCore = 10; - s->maxVertices += 10 * BUTTONWIDGET_MAX; s->OnHacksChanged = HacksSettingsScreen_CheckHacksAllowed; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); HacksSettingsScreen_CheckHacksAllowed(s); s->descriptions[2] = "&eIf &fON&e, then the third person cameras will limit\nðeir zoom distance if they hit a solid block."; @@ -3232,19 +3220,19 @@ static void HacksSettingsScreen_InitWidgets(struct MenuOptionsScreen* s) { } void HacksSettingsScreen_Show(void) { - static struct MenuInputDesc descs[11]; - MenuInput_Float(descs[1], 0.1f, 50, 10); - MenuInput_Float(descs[3], 0.1f, 2048, 1.233f); - MenuInput_Int(descs[9], 1, 179, 70); - MenuOptionsScreen_Show(descs, HacksSettingsScreen_InitWidgets); + MenuInput_Float(menuOpts_descs[1], 0.1f, 50, 10); + MenuInput_Float(menuOpts_descs[3], 0.1f, 2048, 1.233f); + MenuInput_Int(menuOpts_descs[9], 1, 179, 70); + + MenuOptionsScreen_Show(HacksSettingsScreen_InitWidgets); } /*########################################################################################################################* *----------------------------------------------------MiscOptionsScreen----------------------------------------------------* *#########################################################################################################################*/ -static void MiscOptionsScreen_GetReach(cc_string* v) { String_AppendFloat(v, LocalPlayer_Instance.ReachDistance, 2); } -static void MiscOptionsScreen_SetReach(const cc_string* v) { LocalPlayer_Instance.ReachDistance = Menu_Float(v); } +static void MiscOptionsScreen_GetReach(cc_string* v) { String_AppendFloat(v, Entities.CurPlayer->ReachDistance, 2); } +static void MiscOptionsScreen_SetReach(const cc_string* v) { Entities.CurPlayer->ReachDistance = Menu_Float(v); } static void MiscOptionsScreen_GetMusic(cc_string* v) { String_AppendInt(v, Audio_MusicVolume); } static void MiscOptionsScreen_SetMusic(const cc_string* v) { @@ -3293,9 +3281,7 @@ static void MiscSettingsScreen_InitWidgets(struct MenuOptionsScreen* s) { { 1, 50, "Mouse sensitivity", MenuOptionsScreen_Input, MiscOptionsScreen_GetSensitivity, MiscOptionsScreen_SetSensitivity } }; - s->numCore = 7; - s->maxVertices += 7 * BUTTONWIDGET_MAX; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchOptions); /* Disable certain options */ if (!Server.IsSinglePlayer) Menu_Remove(s, 0); @@ -3303,17 +3289,16 @@ static void MiscSettingsScreen_InitWidgets(struct MenuOptionsScreen* s) { } void MiscOptionsScreen_Show(void) { - static struct MenuInputDesc descs[8]; - MenuInput_Float(descs[0], 1, 1024, 5); - MenuInput_Int(descs[1], 0, 100, DEFAULT_MUSIC_VOLUME); - MenuInput_Int(descs[2], 0, 100, DEFAULT_SOUNDS_VOLUME); + MenuInput_Float(menuOpts_descs[0], 1, 1024, 5); + MenuInput_Int(menuOpts_descs[1], 0, 100, DEFAULT_MUSIC_VOLUME); + MenuInput_Int(menuOpts_descs[2], 0, 100, DEFAULT_SOUNDS_VOLUME); #ifdef CC_BUILD_WIN - MenuInput_Int(descs[6], 1, 200, 40); + MenuInput_Int(menuOpts_descs[6], 1, 200, 40); #else - MenuInput_Int(descs[6], 1, 200, 30); + MenuInput_Int(menuOpts_descs[6], 1, 200, 30); #endif - MenuOptionsScreen_Show(descs, MiscSettingsScreen_InitWidgets); + MenuOptionsScreen_Show(MiscSettingsScreen_InitWidgets); } @@ -3326,10 +3311,7 @@ static struct NostalgiaMenuScreen { struct TextWidget title; } NostalgiaMenuScreen; -static struct Widget* nostalgiaMenu_widgets[] = { - (struct Widget*)&NostalgiaMenuScreen.btnA, (struct Widget*)&NostalgiaMenuScreen.btnF, - (struct Widget*)&NostalgiaMenuScreen.done, (struct Widget*)&NostalgiaMenuScreen.title -}; +static struct Widget* nostalgiaMenu_widgets[4]; static void NostalgiaMenuScreen_Appearance(void* a, void* b) { NostalgiaAppearanceScreen_Show(); } static void NostalgiaMenuScreen_Functionality(void* a, void* b) { NostalgiaFunctionalityScreen_Show(); } @@ -3364,12 +3346,13 @@ static void NostalgiaMenuScreen_Init(void* screen) { struct NostalgiaMenuScreen* s = (struct NostalgiaMenuScreen*)screen; s->widgets = nostalgiaMenu_widgets; - s->numWidgets = Array_Elems(nostalgiaMenu_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(nostalgiaMenu_widgets); - TextWidget_Init(&s->title); - ButtonWidget_Init(&s->btnA, 400, NostalgiaMenuScreen_Appearance); - ButtonWidget_Init(&s->btnF, 400, NostalgiaMenuScreen_Functionality); - ButtonWidget_Init(&s->done, 400, NostalgiaMenuScreen_SwitchBack); + ButtonWidget_Add(s, &s->btnA, 400, NostalgiaMenuScreen_Appearance); + ButtonWidget_Add(s, &s->btnF, 400, NostalgiaMenuScreen_Functionality); + ButtonWidget_Add(s, &s->done, 400, NostalgiaMenuScreen_SwitchBack); + TextWidget_Add(s, &s->title); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -3434,14 +3417,12 @@ static void NostalgiaAppearanceScreen_InitWidgets(struct MenuOptionsScreen* s) { { 1, 50, "Classic options", MenuOptionsScreen_Bool, NostalgiaScreen_GetOpts, NostalgiaScreen_SetOpts }, }; - s->numCore = Array_Elems(buttons); - s->maxVertices += Array_Elems(buttons) * BUTTONWIDGET_MAX; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchNostalgia); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchNostalgia); } void NostalgiaAppearanceScreen_Show(void) { - MenuOptionsScreen_Show(NULL, NostalgiaAppearanceScreen_InitWidgets); + MenuOptionsScreen_Show(NostalgiaAppearanceScreen_InitWidgets); } @@ -3473,7 +3454,7 @@ static void NostalgiaScreen_Version(void* screen, void* widget) { Options_SetInt(OPT_GAME_VERSION, ver); GameVersion_Load(); - MenuOptionsScreen_Update(s, Screen_Index(s, widget)); + MenuOptionsScreen_Update(s, widget); } static void NostalgiaScreen_GetVersion(cc_string* v) { String_AppendConst(v, Game_Version.Name); } @@ -3496,14 +3477,11 @@ static void NostalgiaFunctionalityScreen_InitWidgets(struct MenuOptionsScreen* s { 1, 0, "Game version", NostalgiaScreen_Version, NostalgiaScreen_GetVersion, NostalgiaScreen_SetVersion } }; - s->numCore = Array_Elems(buttons) + 1; - s->maxVertices += Array_Elems(buttons) * BUTTONWIDGET_MAX + TEXTWIDGET_MAX; s->DoRecreateExtra = NostalgiaScreen_RecreateExtra; - MenuOptionsScreen_InitButtons(s, buttons, Array_Elems(buttons), Menu_SwitchNostalgia); - TextWidget_Init(&nostalgia_desc); + MenuOptionsScreen_AddButtons(s, buttons, Array_Elems(buttons), Menu_SwitchNostalgia); + TextWidget_Add(s, &nostalgia_desc); Widget_SetLocation(&nostalgia_desc, ANCHOR_CENTRE, ANCHOR_CENTRE, 0, 100); - s->widgets[4] = (struct Widget*)&nostalgia_desc; NostalgiaScreen_UpdateVersionDisabled(); s->descriptions[3] = \ @@ -3513,18 +3491,19 @@ static void NostalgiaFunctionalityScreen_InitWidgets(struct MenuOptionsScreen* s } void NostalgiaFunctionalityScreen_Show(void) { - MenuOptionsScreen_Show(NULL, NostalgiaFunctionalityScreen_InitWidgets); + MenuOptionsScreen_Show(NostalgiaFunctionalityScreen_InitWidgets); } /*########################################################################################################################* *---------------------------------------------------------Overlay---------------------------------------------------------* *#########################################################################################################################*/ -static void Overlay_InitLabels(struct TextWidget* labels) { +static void Overlay_AddLabels(void* screen, struct TextWidget* labels) { int i; - TextWidget_Init(&labels[0]); - for (i = 1; i < 4; i++) { - TextWidget_Init(&labels[i]); + TextWidget_Add(screen, &labels[0]); + for (i = 1; i < 4; i++) + { + TextWidget_Add(screen, &labels[i]); labels[i].color = PackedCol_Make(224, 224, 224, 255); } } @@ -3552,9 +3531,10 @@ static struct TexIdsOverlay { struct TextAtlas idAtlas; struct TextWidget title; } TexIdsOverlay; -static struct Widget* texids_widgets[1] = { (struct Widget*)&TexIdsOverlay.title }; +static struct Widget* texids_widgets[1]; -#define TEXIDS_MAX_PER_PAGE (ATLAS2D_TILES_PER_ROW * ATLAS2D_TILES_PER_ROW) +#define TEXIDS_MAX_ROWS_PER_PAGE 16 +#define TEXIDS_MAX_PER_PAGE (TEXIDS_MAX_ROWS_PER_PAGE * ATLAS2D_TILES_PER_ROW) #define TEXIDS_TEXT_VERTICES (10 * 4 + 90 * 8 + 412 * 12) /* '0'-'9' + '10'-'99' + '100'-'511' */ #define TEXIDS_MAX_VERTICES (TEXTWIDGET_MAX + 4 * ATLAS1D_MAX_ATLASES + TEXIDS_TEXT_VERTICES) @@ -3608,17 +3588,17 @@ static void TexIdsOverlay_BuildTerrain(struct TexIdsOverlay* s, struct VertexTex baseLoc = 0; xOffset = s->xOffset; - tex.uv.U1 = 0.0f; tex.uv.U2 = UV2_Scale; - tex.Width = size; tex.Height = size; + tex.uv.u1 = 0.0f; tex.uv.u2 = UV2_Scale; + tex.width = size; tex.height = size; - for (row = 0; row < Atlas2D.RowsCount; row += ATLAS2D_TILES_PER_ROW) { + for (row = 0; row < Atlas2D.RowsCount; row += TEXIDS_MAX_ROWS_PER_PAGE) { for (i = 0; i < TEXIDS_MAX_PER_PAGE; i++) { tex.x = xOffset + Atlas2D_TileX(i) * size; tex.y = s->yOffset + Atlas2D_TileY(i) * size; - tex.uv.V1 = Atlas1D_RowId(i + baseLoc) * Atlas1D.InvTileSize; - tex.uv.V2 = tex.uv.V1 + UV2_Scale * Atlas1D.InvTileSize; + tex.uv.v1 = Atlas1D_RowId(i + baseLoc) * Atlas1D.InvTileSize; + tex.uv.v2 = tex.uv.v1 + UV2_Scale * Atlas1D.InvTileSize; Gfx_Make2DQuad(&tex, PACKEDCOL_WHITE, ptr); } @@ -3639,8 +3619,8 @@ static void TexIdsOverlay_BuildText(struct TexIdsOverlay* s, struct VertexTextur idAtlas = &s->idAtlas; beg = *ptr; - for (row = 0; row < Atlas2D.RowsCount; row += ATLAS2D_TILES_PER_ROW) { - idAtlas->tex.y = s->yOffset + (size - idAtlas->tex.Height); + for (row = 0; row < Atlas2D.RowsCount; row += TEXIDS_MAX_ROWS_PER_PAGE) { + idAtlas->tex.y = s->yOffset + (size - idAtlas->tex.height); for (y = 0; y < ATLAS2D_TILES_PER_ROW; y++) { for (x = 0; x < ATLAS2D_TILES_PER_ROW; x++) { @@ -3690,10 +3670,11 @@ static void TexIdsOverlay_OnAtlasChanged(void* screen) { static void TexIdsOverlay_Init(void* screen) { struct TexIdsOverlay* s = (struct TexIdsOverlay*)screen; s->widgets = texids_widgets; - s->numWidgets = Array_Elems(texids_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(texids_widgets); s->maxVertices = TEXIDS_MAX_VERTICES; - TextWidget_Init(&s->title); + TextWidget_Add(s, &s->title); Event_Register_(&TextureEvents.AtlasChanged, s, TexIdsOverlay_OnAtlasChanged); } @@ -3702,7 +3683,7 @@ static void TexIdsOverlay_Free(void* screen) { Event_Unregister_(&TextureEvents.AtlasChanged, s, TexIdsOverlay_OnAtlasChanged); } -static void TexIdsOverlay_Render(void* screen, double delta) { +static void TexIdsOverlay_Render(void* screen, float delta) { struct TexIdsOverlay* s = (struct TexIdsOverlay*)screen; int offset = 0; Menu_RenderBounds(); @@ -3750,11 +3731,7 @@ static struct UrlWarningOverlay { char _urlBuffer[STRING_SIZE * 4]; } UrlWarningOverlay; -static struct Widget* urlwarning_widgets[] = { - (struct Widget*)&UrlWarningOverlay.lbls[0], (struct Widget*)&UrlWarningOverlay.lbls[1], - (struct Widget*)&UrlWarningOverlay.lbls[2], (struct Widget*)&UrlWarningOverlay.lbls[3], - (struct Widget*)&UrlWarningOverlay.btns[0], (struct Widget*)&UrlWarningOverlay.btns[1] -}; +static struct Widget* urlwarning_widgets[4 + 2]; static void UrlWarningOverlay_OpenUrl(void* screen, void* b) { struct UrlWarningOverlay* s = (struct UrlWarningOverlay*)screen; @@ -3797,11 +3774,12 @@ static void UrlWarningOverlay_Layout(void* screen) { static void UrlWarningOverlay_Init(void* screen) { struct UrlWarningOverlay* s = (struct UrlWarningOverlay*)screen; s->widgets = urlwarning_widgets; - s->numWidgets = Array_Elems(urlwarning_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(urlwarning_widgets); - Overlay_InitLabels(s->lbls); - ButtonWidget_Init(&s->btns[0], 160, UrlWarningOverlay_OpenUrl); - ButtonWidget_Init(&s->btns[1], 160, UrlWarningOverlay_AppendUrl); + Overlay_AddLabels(s, s->lbls); + ButtonWidget_Add(s, &s->btns[0], 160, UrlWarningOverlay_OpenUrl); + ButtonWidget_Add(s, &s->btns[1], 160, UrlWarningOverlay_AppendUrl); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -3840,14 +3818,12 @@ static struct TexPackOverlay { char _urlBuffer[URL_MAX_SIZE]; } TexPackOverlay; -static struct Widget* texpack_widgets[] = { - (struct Widget*)&TexPackOverlay.lbls[0], (struct Widget*)&TexPackOverlay.lbls[1], - (struct Widget*)&TexPackOverlay.lbls[2], (struct Widget*)&TexPackOverlay.lbls[3], - (struct Widget*)&TexPackOverlay.btns[0], (struct Widget*)&TexPackOverlay.btns[1], - (struct Widget*)&TexPackOverlay.btns[2], (struct Widget*)&TexPackOverlay.btns[3] -}; +static struct Widget* texpack_widgets[4 + 4]; -static cc_bool TexPackOverlay_IsAlways(void* screen, void* w) { return Screen_Index(screen, w) >= 6; } +static cc_bool TexPackOverlay_IsAlways(void* screen, void* w) { + struct ButtonWidget* btn = (struct ButtonWidget*)w; + return btn->meta.val != 0; +} static void TexPackOverlay_YesClick(void* screen, void* widget) { struct TexPackOverlay* s = (struct TexPackOverlay*)screen; @@ -3910,7 +3886,7 @@ static void TexPackOverlay_UpdateLine3(struct TexPackOverlay* s) { } } -static void TexPackOverlay_Update(void* screen, double delta) { +static void TexPackOverlay_Update(void* screen, float delta) { struct TexPackOverlay* s = (struct TexPackOverlay*)screen; struct HttpRequest item; if (!Http_GetResult(s->reqID, &item)) return; @@ -3954,6 +3930,8 @@ static void TexPackOverlay_ContextRecreated(void* screen) { ButtonWidget_SetConst(&s->btns[3], "Always no", &titleFont); s->btns[2].MenuClick = TexPackOverlay_YesClick; s->btns[3].MenuClick = TexPackOverlay_NoClick; + s->btns[2].meta.val = 1; + s->btns[3].meta.val = 1; } s->numWidgets = s->deny ? 6 : 8; @@ -3971,17 +3949,18 @@ static void TexPackOverlay_Layout(void* screen) { static void TexPackOverlay_Init(void* screen) { struct TexPackOverlay* s = (struct TexPackOverlay*)screen; s->widgets = texpack_widgets; - s->numWidgets = Array_Elems(texpack_widgets); + s->numWidgets = 0; + s->maxWidgets = Array_Elems(texpack_widgets); s->contentLength = 0; s->gotContent = false; - s->deny = false; - Overlay_InitLabels(s->lbls); + s->deny = false; + Overlay_AddLabels(s, s->lbls); - ButtonWidget_Init(&s->btns[0], 160, NULL); - ButtonWidget_Init(&s->btns[1], 160, NULL); - ButtonWidget_Init(&s->btns[2], 160, NULL); - ButtonWidget_Init(&s->btns[3], 160, NULL); + ButtonWidget_Add(s, &s->btns[0], 160, NULL); + ButtonWidget_Add(s, &s->btns[1], 160, NULL); + ButtonWidget_Add(s, &s->btns[2], 160, NULL); + ButtonWidget_Add(s, &s->btns[3], 160, NULL); s->maxVertices = Screen_CalcDefaultMaxVertices(s); } @@ -4006,410 +3985,3 @@ void TexPackOverlay_Show(const cc_string* url) { s->reqID = Http_AsyncGetHeaders(url, HTTP_FLAG_PRIORITY); Gui_Add((struct Screen*)s, GUI_PRIORITY_TEXPACK); } - - -#ifdef CC_BUILD_TOUCH -/*########################################################################################################################* -*---------------------------------------------------TouchControlsScreen---------------------------------------------------* -*#########################################################################################################################*/ -#define ONSCREEN_PAGE_BTNS 8 -static struct TouchOnscreenScreen { - Screen_Body - int offset; - struct ButtonWidget back, left, right; - struct ButtonWidget btns[ONSCREEN_PAGE_BTNS]; - const struct SimpleButtonDesc* btnDescs; - struct FontDesc font; -} TouchOnscreenScreen; - -static struct Widget* touchOnscreen_widgets[3 + ONSCREEN_PAGE_BTNS] = { - (struct Widget*)&TouchOnscreenScreen.back, (struct Widget*)&TouchOnscreenScreen.left, - (struct Widget*)&TouchOnscreenScreen.right, (struct Widget*)&TouchOnscreenScreen.btns[0], - (struct Widget*)&TouchOnscreenScreen.btns[1], (struct Widget*)&TouchOnscreenScreen.btns[2], - (struct Widget*)&TouchOnscreenScreen.btns[3], (struct Widget*)&TouchOnscreenScreen.btns[4], - (struct Widget*)&TouchOnscreenScreen.btns[5], (struct Widget*)&TouchOnscreenScreen.btns[6], - (struct Widget*)&TouchOnscreenScreen.btns[7] -}; - -static void TouchOnscreen_UpdateColors(struct TouchOnscreenScreen* s) { - PackedCol grey = PackedCol_Make(0x7F, 0x7F, 0x7F, 0xFF); - int i, j; - - for (i = 0, j = s->offset; i < ONSCREEN_PAGE_BTNS; i++, j++) - { - s->btns[i].color = (Gui._onscreenButtons & (1 << j)) ? PACKEDCOL_WHITE : grey; - } -} - -static void TouchOnscreen_Any(void* screen, void* w) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - int bit = 1 << (Screen_Index(s, w) - 3 + s->offset); - if (Gui._onscreenButtons & bit) { - Gui._onscreenButtons &= ~bit; - } else { - Gui._onscreenButtons |= bit; - } - - Options_SetInt(OPT_TOUCH_BUTTONS, Gui._onscreenButtons); - TouchOnscreen_UpdateColors(s); - TouchScreen_Refresh(); -} -static void TouchOnscreen_More(void* s, void* w) { TouchCtrlsScreen_Show(); } - -static const struct SimpleButtonDesc touchOnscreen_page1[ONSCREEN_PAGE_BTNS] = { - { -120, -50, "Chat", TouchOnscreen_Any }, { 120, -50, "Tablist", TouchOnscreen_Any }, - { -120, 0, "Spawn", TouchOnscreen_Any }, { 120, 0, "Set spawn", TouchOnscreen_Any }, - { -120, 50, "Fly", TouchOnscreen_Any }, { 120, 50, "Noclip", TouchOnscreen_Any }, - { -120, 100, "Speed", TouchOnscreen_Any }, { 120, 100, "Half speed", TouchOnscreen_Any } -}; -static const struct SimpleButtonDesc touchOnscreen_page2[ONSCREEN_PAGE_BTNS] = { - { -120, -50, "Third person", TouchOnscreen_Any }, { 120, -50, "Delete", TouchOnscreen_Any }, - { -120, 0, "Pick", TouchOnscreen_Any }, { 120, 0, "Place", TouchOnscreen_Any }, - { -120, 50, "Switch hotbar", TouchOnscreen_Any }, { 120, 50, "---", TouchOnscreen_Any }, - { -120, 100, "---", TouchOnscreen_Any }, { 120, 100, "---", TouchOnscreen_Any } -}; - -static void TouchOnscreen_SetPage(struct TouchOnscreenScreen* s, cc_bool page1) { - s->offset = page1 ? 0 : ONSCREEN_PAGE_BTNS; - s->btnDescs = page1 ? touchOnscreen_page1 : touchOnscreen_page2; - Menu_InitButtons(s->btns, 200, s->btnDescs, ONSCREEN_PAGE_BTNS); - - Widget_SetDisabled(&s->left, page1); - Widget_SetDisabled(&s->right, !page1); -} - -static void TouchOnscreen_Left(void* screen, void* b) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - TouchOnscreen_SetPage(s, true); - Gui_Refresh((struct Screen*)s); - TouchOnscreen_UpdateColors(s); -} - -static void TouchOnscreen_Right(void* screen, void* b) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - TouchOnscreen_SetPage(s, false); - Gui_Refresh((struct Screen*)s); - TouchOnscreen_UpdateColors(s); -} - -static void TouchOnscreenScreen_ContextLost(void* screen) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - Font_Free(&s->font); - Screen_ContextLost(screen); -} - -static void TouchOnscreenScreen_ContextRecreated(void* screen) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - Gui_MakeTitleFont(&s->font); - Screen_UpdateVb(screen); - Menu_SetButtons(s->btns, &s->font, s->btnDescs, ONSCREEN_PAGE_BTNS); - ButtonWidget_SetConst(&s->back, "Done", &s->font); - ButtonWidget_SetConst(&s->left, "<", &s->font); - ButtonWidget_SetConst(&s->right, ">", &s->font); -} - -static void TouchOnscreenScreen_Layout(void* screen) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - Menu_LayoutButtons(s->btns, s->btnDescs, ONSCREEN_PAGE_BTNS); - Menu_LayoutBack(&s->back); - Widget_SetLocation(&s->left, ANCHOR_CENTRE, ANCHOR_CENTRE, -260, 0); - Widget_SetLocation(&s->right, ANCHOR_CENTRE, ANCHOR_CENTRE, 260, 0); -} - -static void TouchOnscreenScreen_Init(void* screen) { - struct TouchOnscreenScreen* s = (struct TouchOnscreenScreen*)screen; - s->widgets = touchOnscreen_widgets; - s->numWidgets = Array_Elems(touchOnscreen_widgets); - - ButtonWidget_Init(&s->back, 400, TouchOnscreen_More); - ButtonWidget_Init(&s->left, 40, TouchOnscreen_Left); - ButtonWidget_Init(&s->right, 40, TouchOnscreen_Right); - TouchOnscreen_SetPage(s, true); - TouchOnscreen_UpdateColors(screen); - - s->maxVertices = Screen_CalcDefaultMaxVertices(s); -} - -static const struct ScreenVTABLE TouchOnscreenScreen_VTABLE = { - TouchOnscreenScreen_Init, Screen_NullUpdate, Screen_NullFunc, - MenuScreen_Render2, Screen_BuildMesh, - Menu_InputDown, Screen_InputUp, Screen_TKeyPress, Screen_TText, - Menu_PointerDown, Screen_PointerUp, Menu_PointerMove, Screen_TMouseScroll, - TouchOnscreenScreen_Layout, TouchOnscreenScreen_ContextLost, TouchOnscreenScreen_ContextRecreated -}; -void TouchOnscreenScreen_Show(void) { - struct TouchOnscreenScreen* s = &TouchOnscreenScreen; - s->grabsInput = true; - s->closable = true; - s->VTABLE = &TouchOnscreenScreen_VTABLE; - - Gui_Add((struct Screen*)s, GUI_PRIORITY_TOUCHMORE); -} - - -/*########################################################################################################################* -*---------------------------------------------------TouchControlsScreen---------------------------------------------------* -*#########################################################################################################################*/ -#define TOUCHCTRLS_BTNS 5 -static struct TouchCtrlsScreen { - Screen_Body - struct ButtonWidget back; - struct ButtonWidget btns[TOUCHCTRLS_BTNS]; - struct FontDesc font; -} TouchCtrlsScreen; - -static struct Widget* touchCtrls_widgets[1 + TOUCHCTRLS_BTNS] = { - (struct Widget*)&TouchCtrlsScreen.back, (struct Widget*)&TouchCtrlsScreen.btns[0], - (struct Widget*)&TouchCtrlsScreen.btns[1], (struct Widget*)&TouchCtrlsScreen.btns[2], - (struct Widget*)&TouchCtrlsScreen.btns[3], (struct Widget*)&TouchCtrlsScreen.btns[4] -}; - -static const char* GetTapDesc(int mode) { - if (mode == INPUT_MODE_PLACE) return "Tap: Place"; - if (mode == INPUT_MODE_DELETE) return "Tap: Delete"; - return "Tap: None"; -} -static void TouchCtrls_UpdateTapText(void* screen) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - ButtonWidget_SetConst(&s->btns[0], GetTapDesc(Input_TapMode), &s->font); - s->dirty = true; -} - -static const char* GetHoldDesc(int mode) { - if (mode == INPUT_MODE_PLACE) return "Hold: Place"; - if (mode == INPUT_MODE_DELETE) return "Hold: Delete"; - return "Hold: None"; -} -static void TouchCtrls_UpdateHoldText(void* screen) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - ButtonWidget_SetConst(&s->btns[1], GetHoldDesc(Input_HoldMode), &s->font); - s->dirty = true; -} - -static void TouchCtrls_UpdateSensitivity(void* screen) { - cc_string value; char valueBuffer[STRING_SIZE]; - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - String_InitArray(value, valueBuffer); - - String_AppendConst(&value, "Sensitivity: "); - MiscOptionsScreen_GetSensitivity(&value); - ButtonWidget_Set(&s->btns[2], &value, &s->font); - s->dirty = true; -} - -static void TouchCtrls_UpdateScale(void* screen) { - cc_string value; char valueBuffer[STRING_SIZE]; - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - String_InitArray(value, valueBuffer); - - String_AppendConst(&value, "Scale: "); - String_AppendFloat(&value, Gui.RawTouchScale, 1); - ButtonWidget_Set(&s->btns[3], &value, &s->font); - s->dirty = true; -} - -static void TouchCtrls_More(void* s, void* w) { TouchMoreScreen_Show(); } -static void TouchCtrls_Onscreen(void* s, void* w) { TouchOnscreenScreen_Show(); } - -static void TouchCtrls_Tap(void* s, void* w) { - Input_TapMode = (Input_TapMode + 1) % INPUT_MODE_COUNT; - TouchCtrls_UpdateTapText(s); -} -static void TouchCtrls_Hold(void* s, void* w) { - Input_HoldMode = (Input_HoldMode + 1) % INPUT_MODE_COUNT; - TouchCtrls_UpdateHoldText(s); -} - -static void TouchCtrls_SensitivityDone(const cc_string* value, cc_bool valid) { - if (!valid) return; - MiscOptionsScreen_SetSensitivity(value); - TouchCtrls_UpdateSensitivity(&TouchCtrlsScreen); -} - -static void TouchCtrls_Sensitivity(void* screen, void* w) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - static struct MenuInputDesc desc; - cc_string value; char valueBuffer[STRING_SIZE]; - String_InitArray(value, valueBuffer); - - MenuInput_Int(desc, 1, 200, 30); - MiscOptionsScreen_GetSensitivity(&value); - MenuInputOverlay_Show(&desc, &value, TouchCtrls_SensitivityDone, true); - /* Fix Sensitivity button getting stuck as 'active' */ - /* (input overlay swallows subsequent pointer events) */ - s->btns[2].active = 0; -} - -static void TouchCtrls_ScaleDone(const cc_string* value, cc_bool valid) { - if (!valid) return; - ChatOptionsScreen_SetScale(value, &Gui.RawTouchScale, OPT_TOUCH_SCALE); - TouchCtrls_UpdateScale(&TouchCtrlsScreen); -} - -static void TouchCtrls_Scale(void* screen, void* w) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - static struct MenuInputDesc desc; - cc_string value; char valueBuffer[STRING_SIZE]; - String_InitArray(value, valueBuffer); - - MenuInput_Float(desc, 0.25f, 5.0f, 1.0f); - String_AppendFloat(&value, Gui.RawTouchScale, 1); - MenuInputOverlay_Show(&desc, &value, TouchCtrls_ScaleDone, true); - s->btns[3].active = 0; -} - -static const struct SimpleButtonDesc touchCtrls_btns[5] = { - { -102, -50, "", TouchCtrls_Tap }, - { 102, -50, "", TouchCtrls_Hold }, - { -102, 0, "", TouchCtrls_Sensitivity }, - { 102, 0, "", TouchCtrls_Scale }, - { 0, 50, "On-screen controls", TouchCtrls_Onscreen } -}; - -static void TouchCtrlsScreen_ContextLost(void* screen) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - Font_Free(&s->font); - Screen_ContextLost(screen); -} - -static void TouchCtrlsScreen_ContextRecreated(void* screen) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - Gui_MakeTitleFont(&s->font); - Screen_UpdateVb(screen); - Menu_SetButtons(s->btns, &s->font, touchCtrls_btns, TOUCHCTRLS_BTNS); - ButtonWidget_SetConst(&s->back, "Done", &s->font); - - TouchCtrls_UpdateTapText(s); - TouchCtrls_UpdateHoldText(s); - TouchCtrls_UpdateSensitivity(s); - TouchCtrls_UpdateScale(s); -} - -static void TouchCtrlsScreen_Layout(void* screen) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - Menu_LayoutButtons(s->btns, touchCtrls_btns, TOUCHCTRLS_BTNS); - Menu_LayoutBack(&s->back); -} - -static void TouchCtrlsScreen_Init(void* screen) { - struct TouchCtrlsScreen* s = (struct TouchCtrlsScreen*)screen; - s->widgets = touchCtrls_widgets; - s->numWidgets = Array_Elems(touchCtrls_widgets); - - Menu_InitButtons(s->btns, 195, touchCtrls_btns, 4); - Menu_InitButtons(s->btns + 4, 400, touchCtrls_btns + 4, 1); - ButtonWidget_Init(&s->back, 400, TouchCtrls_More); - - s->maxVertices = Screen_CalcDefaultMaxVertices(s); -} - -static const struct ScreenVTABLE TouchCtrlsScreen_VTABLE = { - TouchCtrlsScreen_Init, Screen_NullUpdate, Screen_NullFunc, - MenuScreen_Render2, Screen_BuildMesh, - Menu_InputDown, Screen_InputUp, Screen_TKeyPress, Screen_TText, - Menu_PointerDown, Screen_PointerUp, Menu_PointerMove, Screen_TMouseScroll, - TouchCtrlsScreen_Layout, TouchCtrlsScreen_ContextLost, TouchCtrlsScreen_ContextRecreated -}; -void TouchCtrlsScreen_Show(void) { - struct TouchCtrlsScreen* s = &TouchCtrlsScreen; - s->grabsInput = true; - s->closable = true; - s->VTABLE = &TouchCtrlsScreen_VTABLE; - - Gui_Add((struct Screen*)s, GUI_PRIORITY_TOUCHMORE); -} - - -/*########################################################################################################################* -*-----------------------------------------------------TouchMoreScreen-----------------------------------------------------* -*#########################################################################################################################*/ -#define TOUCHMORE_BTNS 6 -static struct TouchMoreScreen { - Screen_Body - struct ButtonWidget back; - struct ButtonWidget btns[TOUCHMORE_BTNS]; -} TouchMoreScreen; - -static struct Widget* touchMore_widgets[1 + TOUCHMORE_BTNS] = { - (struct Widget*)&TouchMoreScreen.back, (struct Widget*)&TouchMoreScreen.btns[0], - (struct Widget*)&TouchMoreScreen.btns[1], (struct Widget*)&TouchMoreScreen.btns[2], - (struct Widget*)&TouchMoreScreen.btns[3], (struct Widget*)&TouchMoreScreen.btns[4], - (struct Widget*)&TouchMoreScreen.btns[5] -}; - -static void TouchMore_Take(void* s, void* w) { - Gui_Remove((struct Screen*)&TouchMoreScreen); - Game_ScreenshotRequested = true; -} -static void TouchMore_Screen(void* s, void* w) { - Gui_Remove((struct Screen*)&TouchMoreScreen); - Game_ToggleFullscreen(); -} -static void TouchMore_Ctrls(void* s, void* w) { TouchCtrlsScreen_Show(); } -static void TouchMore_Menu(void* s, void* w) { - Gui_Remove((struct Screen*)&TouchMoreScreen); - Gui_ShowPauseMenu(); -} -static void TouchMore_Game(void* s, void* w) { - Gui_Remove((struct Screen*)&TouchMoreScreen); -} -static void TouchMore_Chat(void* s, void* w) { - Gui_Remove((struct Screen*)&TouchMoreScreen); - ChatScreen_OpenInput(&String_Empty); -} -static void TouchMore_Fog(void* s, void* w) { Game_CycleViewDistance(); } - -static const struct SimpleButtonDesc touchMore_btns[TOUCHMORE_BTNS] = { - { -102, -50, "Screenshot", TouchMore_Take }, - { -102, 0, "Fullscreen", TouchMore_Screen }, - { 102, -50, "Chat", TouchMore_Chat }, - { 102, 0, "Fog", TouchMore_Fog }, - { 0, 50, "Controls", TouchMore_Ctrls }, - { 0, 100, "Main menu", TouchMore_Menu } -}; - -static void TouchMoreScreen_ContextRecreated(void* screen) { - struct TouchMoreScreen* s = (struct TouchMoreScreen*)screen; - struct FontDesc titleFont; - Gui_MakeTitleFont(&titleFont); - Screen_UpdateVb(screen); - - Menu_SetButtons(s->btns, &titleFont, touchMore_btns, TOUCHMORE_BTNS); - ButtonWidget_SetConst(&s->back, "Back to game", &titleFont); - Font_Free(&titleFont); -} - -static void TouchMoreScreen_Layout(void* screen) { - struct TouchMoreScreen* s = (struct TouchMoreScreen*)screen; - Menu_LayoutButtons(s->btns, touchMore_btns, TOUCHMORE_BTNS); - Menu_LayoutBack(&s->back); -} - -static void TouchMoreScreen_Init(void* screen) { - struct TouchMoreScreen* s = (struct TouchMoreScreen*)screen; - s->widgets = touchMore_widgets; - s->numWidgets = Array_Elems(touchMore_widgets); - - Menu_InitButtons(s->btns, 195, touchMore_btns, 4); - Menu_InitButtons(s->btns + 4, 400, touchMore_btns + 4, 2); - ButtonWidget_Init(&s->back, 400, TouchMore_Game); - - s->maxVertices = Screen_CalcDefaultMaxVertices(s); -} - -static const struct ScreenVTABLE TouchMoreScreen_VTABLE = { - TouchMoreScreen_Init, Screen_NullUpdate, Screen_NullFunc, - MenuScreen_Render2, Screen_BuildMesh, - Menu_InputDown, Screen_InputUp, Screen_TKeyPress, Screen_TText, - Menu_PointerDown, Screen_PointerUp, Menu_PointerMove, Screen_TMouseScroll, - TouchMoreScreen_Layout, Screen_ContextLost, TouchMoreScreen_ContextRecreated -}; -void TouchMoreScreen_Show(void) { - struct TouchMoreScreen* s = &TouchMoreScreen; - s->grabsInput = true; - s->closable = true; - s->VTABLE = &TouchMoreScreen_VTABLE; - - Gui_Add((struct Screen*)s, GUI_PRIORITY_TOUCHMORE); -} -#endif \ No newline at end of file diff --git a/src/Menus.h b/src/Menus.h index 78346ce47..84f55d122 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -1,15 +1,28 @@ #ifndef CC_MENUS_H #define CC_MENUS_H -#include "Core.h" +#include "Gui.h" /* Contains all 2D menu screen implementations. Copyright 2014-2023 ClassiCube | Licensed under BSD-3 */ struct Screen; +struct MenuInputDesc; +struct FontDesc; +struct ButtonWidget; + int Menu_InputDown(void* screen, int key); int Menu_PointerDown(void* screen, int id, int x, int y); int Menu_PointerMove(void* screen, int id, int x, int y); +struct SimpleButtonDesc { short x, y; const char* title; Widget_LeftClick onClick; }; +void Menu_AddButtons(void* screen, struct ButtonWidget* btns, int width, + const struct SimpleButtonDesc* descs, int count); +void Menu_LayoutButtons(struct ButtonWidget* btns, + const struct SimpleButtonDesc* descs, int count); +void Menu_SetButtons(struct ButtonWidget* btns, struct FontDesc* font, + const struct SimpleButtonDesc* descs, int count); +void Menu_LayoutBack(struct ButtonWidget* btn); + void PauseScreen_Show(void); void OptionsGroupScreen_Show(void); void ClassicOptionsScreen_Show(void); @@ -49,4 +62,8 @@ void TouchCtrlsScreen_Show(void); void TouchMoreScreen_Show(void); void TouchOnscreenScreen_Show(void); #endif + +void MenuScreen_Render2(void* screen, float delta); +typedef void (*MenuInputDone)(const cc_string* value, cc_bool valid); +void MenuInputOverlay_Show(struct MenuInputDesc* desc, const cc_string* value, MenuInputDone onDone, cc_bool screenMode); #endif diff --git a/src/Model.c b/src/Model.c index 740c228c7..387f0ec12 100644 --- a/src/Model.c +++ b/src/Model.c @@ -98,7 +98,8 @@ void Model_Render(struct Model* model, struct Entity* e) { Vec3 pos = e->Position; if (model->bobbing) pos.y += e->Anim.BobbingModel; /* Original classic offsets models slightly into ground */ - if (Game_ClassicMode) pos.y -= 1.5f / 16.0f; + if (Game_ClassicMode && (e->Flags & ENTITY_FLAG_CLASSIC_ADJUST)) + pos.y -= 1.5f / 16.0f; Model_SetupState(model, e); Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); @@ -181,7 +182,7 @@ static struct VertexTextured* real_vertices; static GfxResourceID modelVB; void Model_LockVB(struct Entity* entity, int verticesCount) { -#ifdef CC_BUILD_LOWMEM +#ifdef CC_BUILD_CONSOLE if (!entity->ModelVB) { entity->ModelVB = Gfx_CreateDynamicVb(VERTEX_FORMAT_TEXTURED, Models.Active->maxVertices); } @@ -216,8 +217,8 @@ void Model_DrawPart(struct ModelPart* part) { dst->x = v.x; dst->y = v.y; dst->z = v.z; dst->Col = Models.Cols[i >> 2]; - dst->U = (v.U & UV_POS_MASK) * Models.uScale - (v.U >> UV_MAX_SHIFT) * 0.01f * Models.uScale; - dst->V = (v.V & UV_POS_MASK) * Models.vScale - (v.V >> UV_MAX_SHIFT) * 0.01f * Models.vScale; + dst->U = (v.u & UV_POS_MASK) * Models.uScale - (v.u >> UV_MAX_SHIFT) * 0.01f * Models.uScale; + dst->V = (v.v & UV_POS_MASK) * Models.vScale - (v.v >> UV_MAX_SHIFT) * 0.01f * Models.vScale; src++; dst++; } model->index += count; @@ -270,8 +271,8 @@ void Model_DrawRotate(float angleX, float angleY, float angleZ, struct ModelPart dst->x = v.x + x; dst->y = v.y + y; dst->z = v.z + z; dst->Col = Models.Cols[i >> 2]; - dst->U = (v.U & UV_POS_MASK) * Models.uScale - (v.U >> UV_MAX_SHIFT) * 0.01f * Models.uScale; - dst->V = (v.V & UV_POS_MASK) * Models.vScale - (v.V >> UV_MAX_SHIFT) * 0.01f * Models.vScale; + dst->U = (v.u & UV_POS_MASK) * Models.uScale - (v.u >> UV_MAX_SHIFT) * 0.01f * Models.uScale; + dst->V = (v.v & UV_POS_MASK) * Models.vScale - (v.v >> UV_MAX_SHIFT) * 0.01f * Models.vScale; src++; dst++; } model->index += count; @@ -479,24 +480,13 @@ void Model_Register(struct Model* model) { } void Model_Unregister(struct Model* model) { + struct Model* cur; int i; - - /* remove the model from the list */ - struct Model* item = models_head; - if (models_head == model) { - models_head = model->next; - } - while (item) { - if (item->next == model) { - item->next = model->next; - } - - models_tail = item; - item = item->next; - } + LinkedList_Remove(model, cur, models_head, models_tail); /* unset this model from all entities, replacing with default fallback */ - for (i = 0; i < ENTITIES_MAX_COUNT; i++) { + for (i = 0; i < ENTITIES_MAX_COUNT; i++) + { struct Entity* e = Entities.List[i]; if (e && e->Model == model) { cc_string humanModelName = String_FromReadonly(Models.Human->name); @@ -2079,20 +2069,20 @@ static void BlockModel_SpriteZQuad(cc_bool firstPart, cc_bool mirror) { xz1 = 0.0f; xz2 = 0.0f; if (firstPart) { /* Need to break into two quads for when drawing a sprite model in hand. */ - if (mirror) { rec.U1 = 0.5f; xz1 = -5.5f/16.0f; } - else { rec.U2 = 0.5f; xz2 = -5.5f/16.0f; } + if (mirror) { rec.u1 = 0.5f; xz1 = -5.5f/16.0f; } + else { rec.u2 = 0.5f; xz2 = -5.5f/16.0f; } } else { - if (mirror) { rec.U2 = 0.5f; xz2 = 5.5f/16.0f; } - else { rec.U1 = 0.5f; xz1 = 5.5f/16.0f; } + if (mirror) { rec.u2 = 0.5f; xz2 = 5.5f/16.0f; } + else { rec.u1 = 0.5f; xz1 = 5.5f/16.0f; } } ptr = bModel_vertices; v.Col = col; - v.x = xz1; v.y = 0.0f; v.z = xz1; v.U = rec.U2; v.V = rec.V2; *ptr++ = v; - v.y = 1.0f; v.V = rec.V1; *ptr++ = v; - v.x = xz2; v.z = xz2; v.U = rec.U1; *ptr++ = v; - v.y = 0.0f; v.V = rec.V2; *ptr++ = v; + v.x = xz1; v.y = 0.0f; v.z = xz1; v.U = rec.u2; v.V = rec.v2; *ptr++ = v; + v.y = 1.0f; v.V = rec.v1; *ptr++ = v; + v.x = xz2; v.z = xz2; v.U = rec.u1; *ptr++ = v; + v.y = 0.0f; v.V = rec.v2; *ptr++ = v; bModel_vertices = ptr; } @@ -2109,20 +2099,20 @@ static void BlockModel_SpriteXQuad(cc_bool firstPart, cc_bool mirror) { x1 = 0.0f; x2 = 0.0f; z1 = 0.0f; z2 = 0.0f; if (firstPart) { - if (mirror) { rec.U2 = 0.5f; x2 = -5.5f/16.0f; z2 = 5.5f/16.0f; } - else { rec.U1 = 0.5f; x1 = -5.5f/16.0f; z1 = 5.5f/16.0f; } + if (mirror) { rec.u2 = 0.5f; x2 = -5.5f/16.0f; z2 = 5.5f/16.0f; } + else { rec.u1 = 0.5f; x1 = -5.5f/16.0f; z1 = 5.5f/16.0f; } } else { - if (mirror) { rec.U1 = 0.5f; x1 = 5.5f/16.0f; z1 = -5.5f/16.0f; } - else { rec.U2 = 0.5f; x2 = 5.5f/16.0f; z2 = -5.5f/16.0f; } + if (mirror) { rec.u1 = 0.5f; x1 = 5.5f/16.0f; z1 = -5.5f/16.0f; } + else { rec.u2 = 0.5f; x2 = 5.5f/16.0f; z2 = -5.5f/16.0f; } } ptr = bModel_vertices; v.Col = col; - v.x = x1; v.y = 0.0f; v.z = z1; v.U = rec.U2; v.V = rec.V2; *ptr++ = v; - v.y = 1.0f; v.V = rec.V1; *ptr++ = v; - v.x = x2; v.z = z2; v.U = rec.U1; *ptr++ = v; - v.y = 0.0f; v.V = rec.V2; *ptr++ = v; + v.x = x1; v.y = 0.0f; v.z = z1; v.U = rec.u2; v.V = rec.v2; *ptr++ = v; + v.y = 1.0f; v.V = rec.v1; *ptr++ = v; + v.x = x2; v.z = z2; v.U = rec.u1; *ptr++ = v; + v.y = 0.0f; v.V = rec.v2; *ptr++ = v; bModel_vertices = ptr; } @@ -2228,6 +2218,7 @@ static void BlockModel_Register(void) { block_model.pushes = false; block_model.maxVertices = BLOCKMODEL_MAX_VERTICES; Model_Register(&block_model); + Models.Block = &block_model; } diff --git a/src/Model.h b/src/Model.h index 7100e30a1..7f074d471 100644 --- a/src/Model.h +++ b/src/Model.h @@ -19,10 +19,10 @@ extern struct IGameComponent Models_Component; enum RotateOrder { ROTATE_ORDER_ZYX, ROTATE_ORDER_XZY, ROTATE_ORDER_YZX, ROTATE_ORDER_XYZ }; /* Describes a vertex within a model. */ -struct ModelVertex { float x, y, z; cc_uint16 U, V; }; +struct ModelVertex { float x, y, z; cc_uint16 u, v; }; static CC_INLINE void ModelVertex_Init(struct ModelVertex* vertex, float x, float y, float z, int u, int v) { vertex->x = x; vertex->y = y; vertex->z = z; - vertex->U = u; vertex->V = v; + vertex->u = u; vertex->v = v; } /* Describes the starting index of this part within a model's array of vertices, @@ -47,7 +47,7 @@ struct Model { /* Name of this model */ const char* name; /* Pointer to the raw vertices of the model */ - struct ModelVertex* vertices; + struct ModelVertex* vertices; /* Pointer to default texture for this model */ struct ModelTex* defaultTex; @@ -112,8 +112,10 @@ CC_VAR extern struct _ModelsData { /* Maximum number of vertices that can be stored in Vertices. */ /* NOTE: If you change this, you MUST also destroy and recreate the dynamic VB. */ int MaxVertices; - /* Pointer to humanoid/human model.*/ + /* Pointer to humanoid/human model */ struct Model* Human; + /* Pointer to block model */ + struct Model* Block; } Models; /* Initialises fields of a model to default. */ diff --git a/src/Options.c b/src/Options.c index 1a8dd4257..cc7083870 100644 --- a/src/Options.c +++ b/src/Options.c @@ -201,7 +201,7 @@ void Options_SetSecure(const char* opt, const cc_string* src) { String_InitArray(enc, encData); res = Platform_Encrypt(src->buffer, src->length, &enc); - if (res) { Platform_Log2("Error %h encrypting option %c", &res, opt); return; } + if (res) { Platform_Log2("Error %e encrypting option %c", &res, opt); return; } /* base64 encode the data, as user might edit options.txt with a text editor */ if (enc.length > 1500) Logger_Abort("too large to base64"); @@ -223,5 +223,5 @@ void Options_GetSecure(const char* opt, cc_string* dst) { dataLen = Convert_FromBase64(raw.buffer, raw.length, data); res = Platform_Decrypt(data, dataLen, dst); - if (res) Platform_Log2("Error %h decrypting option %c", &res, opt); + if (res) Platform_Log2("Error %e decrypting option %c", &res, opt); } diff --git a/src/Options.h b/src/Options.h index e1ad9f023..d52040566 100644 --- a/src/Options.h +++ b/src/Options.h @@ -49,6 +49,8 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 #define OPT_HOTBAR_SCALE "gui-hotbarscale" #define OPT_INVENTORY_SCALE "gui-inventoryscale" #define OPT_CHAT_SCALE "gui-chatscale" +#define OPT_CHAT_AUTO_SCALE "gui-autoscalechat" +#define OPT_CROSSHAIR_SCALE "gui-crosshairscale" #define OPT_SHOW_FPS "gui-showfps" #define OPT_FONT_NAME "gui-fontname" #define OPT_BLACK_TEXT "gui-blacktextshadows" @@ -71,6 +73,7 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 #define OPT_CAMERA_SMOOTH "camera-smooth" #define OPT_GRAB_CURSOR "win-grab-cursor" #define OPT_TOUCH_BUTTONS "gui-touchbuttons" +#define OPT_TOUCH_HALIGN "gui-touch-halign" #define OPT_TOUCH_SCALE "gui-touchscale" #define OPT_HTTP_ONLY "http-no-https" #define OPT_HTTPS_VERIFY "https-verify" @@ -79,6 +82,7 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 #define OPT_DPI_SCALING "win-dpi-scaling" #define OPT_GAME_VERSION "game-version" #define OPT_INV_SCROLLBAR_SCALE "inv-scrollbar-scale" +#define OPT_ANAGLYPH3D "anaglyph-3d" #define OPT_SELECTED_BLOCK_OUTLINE_COLOR "selected-block-outline-color" #define OPT_SELECTED_BLOCK_OUTLINE_OPACITY "selected-block-outline-opacity" diff --git a/src/Particle.c b/src/Particle.c index 2dfc20679..403900c8e 100644 --- a/src/Particle.c +++ b/src/Particle.c @@ -33,10 +33,10 @@ void Particle_DoRender(const Vec2* size, const Vec3* pos, const TextureRec* rec, aX = view->row1.x * sX; aY = view->row2.x * sX; aZ = view->row3.x * sX; /* right * size.x * 0.5f */ bX = view->row1.y * sY; bY = view->row2.y * sY; bZ = view->row3.y * sY; /* up * size.y * 0.5f */ - v->x = centre.x - aX - bX; v->y = centre.y - aY - bY; v->z = centre.z - aZ - bZ; v->Col = col; v->U = rec->U1; v->V = rec->V2; v++; - v->x = centre.x - aX + bX; v->y = centre.y - aY + bY; v->z = centre.z - aZ + bZ; v->Col = col; v->U = rec->U1; v->V = rec->V1; v++; - v->x = centre.x + aX + bX; v->y = centre.y + aY + bY; v->z = centre.z + aZ + bZ; v->Col = col; v->U = rec->U2; v->V = rec->V1; v++; - v->x = centre.x + aX - bX; v->y = centre.y + aY - bY; v->z = centre.z + aZ - bZ; v->Col = col; v->U = rec->U2; v->V = rec->V2; v++; + v->x = centre.x - aX - bX; v->y = centre.y - aY - bY; v->z = centre.z - aZ - bZ; v->Col = col; v->U = rec->u1; v->V = rec->v2; v++; + v->x = centre.x - aX + bX; v->y = centre.y - aY + bY; v->z = centre.z - aZ + bZ; v->Col = col; v->U = rec->u1; v->V = rec->v1; v++; + v->x = centre.x + aX + bX; v->y = centre.y + aY + bY; v->z = centre.z + aZ + bZ; v->Col = col; v->U = rec->u2; v->V = rec->v1; v++; + v->x = centre.x + aX - bX; v->y = centre.y + aY - bY; v->z = centre.z + aZ - bZ; v->Col = col; v->U = rec->u2; v->V = rec->v2; v++; } static cc_bool CollidesHor(Vec3* nextPos, BlockID block) { @@ -97,17 +97,17 @@ static cc_bool IntersectsBlock(struct Particle* p, CanPassThroughFunc canPassThr return !canPassThrough(cur) && p->nextPos.y >= minY && p->nextPos.y < maxY && CollidesHor(&p->nextPos, cur); } -static cc_bool PhysicsTick(struct Particle* p, float gravity, CanPassThroughFunc canPassThrough, double delta) { +static cc_bool PhysicsTick(struct Particle* p, float gravity, CanPassThroughFunc canPassThrough, float delta) { Vec3 velocity; int y, begY, endY; p->lastPos = p->nextPos; if (IntersectsBlock(p, canPassThrough)) return true; - p->velocity.y -= gravity * (float)delta; + p->velocity.y -= gravity * delta; begY = Math_Floor(p->nextPos.y); - Vec3_Mul1(&velocity, &p->velocity, (float)delta * 3.0f); + Vec3_Mul1(&velocity, &p->velocity, delta * 3.0f); Vec3_Add(&p->nextPos, &p->nextPos, &velocity); endY = Math_Floor(p->nextPos.y); @@ -118,7 +118,7 @@ static cc_bool PhysicsTick(struct Particle* p, float gravity, CanPassThroughFunc for (y = begY; y >= endY && ClipY(p, y, true, canPassThrough); y--) {} } - p->lifetime -= (float)delta; + p->lifetime -= delta; return p->lifetime < 0.0f; } @@ -135,7 +135,7 @@ static cc_bool RainParticle_CanPass(BlockID block) { return draw == DRAW_GAS || draw == DRAW_SPRITE; } -static cc_bool RainParticle_Tick(struct Particle* p, double delta) { +static cc_bool RainParticle_Tick(struct Particle* p, float delta) { hitTerrain = false; return PhysicsTick(p, 3.5f, RainParticle_CanPass, delta) || hitTerrain; } @@ -178,7 +178,7 @@ static void Rain_RemoveAt(int i) { rain_count--; } -static void Rain_Tick(double delta) { +static void Rain_Tick(float delta) { int i; for (i = 0; i < rain_count; i++) { if (RainParticle_Tick(&rain_Particles[i], delta)) { @@ -187,6 +187,30 @@ static void Rain_Tick(double delta) { } } +void Particles_RainSnowEffect(float x, float y, float z) { + struct Particle* p; + int i, type; + + for (i = 0; i < 2; i++) { + if (rain_count == PARTICLES_MAX) Rain_RemoveAt(0); + p = &rain_Particles[rain_count++]; + + p->velocity.x = Random_Float(&rnd) * 0.8f - 0.4f; /* [-0.4, 0.4] */ + p->velocity.z = Random_Float(&rnd) * 0.8f - 0.4f; + p->velocity.y = Random_Float(&rnd) + 0.4f; + + p->lastPos.x = x + Random_Float(&rnd); /* [0.0, 1.0] */ + p->lastPos.y = y + Random_Float(&rnd) * 0.1f + 0.01f; + p->lastPos.z = z + Random_Float(&rnd); + + p->nextPos = p->lastPos; + p->lifetime = 40.0f; + + type = Random_Next(&rnd, 30); + p->size = type >= 28 ? 2 : (type >= 25 ? 4 : 3); + } +} + /*########################################################################################################################* *------------------------------------------------------Terrain particle---------------------------------------------------* @@ -208,7 +232,7 @@ static cc_bool TerrainParticle_CanPass(BlockID block) { return draw == DRAW_GAS || draw == DRAW_SPRITE || Blocks.IsLiquid[block]; } -static cc_bool TerrainParticle_Tick(struct TerrainParticle* p, double delta) { +static cc_bool TerrainParticle_Tick(struct TerrainParticle* p, float delta) { return PhysicsTick(&p->base, Blocks.ParticleGravity[p->block], TerrainParticle_CanPass, delta); } @@ -282,7 +306,7 @@ static void Terrain_RemoveAt(int i) { terrain_count--; } -static void Terrain_Tick(double delta) { +static void Terrain_Tick(float delta) { int i; for (i = 0; i < terrain_count; i++) { if (TerrainParticle_Tick(&terrain_particles[i], delta)) { @@ -291,131 +315,6 @@ static void Terrain_Tick(double delta) { } } -/*########################################################################################################################* -*-------------------------------------------------------Custom particle---------------------------------------------------* -*#########################################################################################################################*/ -struct CustomParticle { - struct Particle base; - int effectId; - float totalLifespan; -}; - -struct CustomParticleEffect Particles_CustomEffects[256]; -static struct CustomParticle custom_particles[PARTICLES_MAX]; -static int custom_count; -static cc_uint8 collideFlags; -#define EXPIRES_UPON_TOUCHING_GROUND (1 << 0) -#define SOLID_COLLIDES (1 << 1) -#define LIQUID_COLLIDES (1 << 2) -#define LEAF_COLLIDES (1 << 3) - -static cc_bool CustomParticle_CanPass(BlockID block) { - cc_uint8 draw, collide; - - draw = Blocks.Draw[block]; - if (draw == DRAW_TRANSPARENT_THICK && !(collideFlags & LEAF_COLLIDES)) return true; - - collide = Blocks.Collide[block]; - if (collide == COLLIDE_SOLID && (collideFlags & SOLID_COLLIDES)) return false; - if (collide == COLLIDE_LIQUID && (collideFlags & LIQUID_COLLIDES)) return false; - return true; -} - -static cc_bool CustomParticle_Tick(struct CustomParticle* p, double delta) { - struct CustomParticleEffect* e = &Particles_CustomEffects[p->effectId]; - hitTerrain = false; - collideFlags = e->collideFlags; - - return PhysicsTick(&p->base, e->gravity, CustomParticle_CanPass, delta) - || (hitTerrain && (e->collideFlags & EXPIRES_UPON_TOUCHING_GROUND)); -} - -static void CustomParticle_Render(struct CustomParticle* p, float t, struct VertexTextured* vertices) { - struct CustomParticleEffect* e = &Particles_CustomEffects[p->effectId]; - Vec3 pos; - Vec2 size; - PackedCol col; - TextureRec rec = e->rec; - int x, y, z; - - float time_lived = p->totalLifespan - p->base.lifetime; - int curFrame = Math_Floor(e->frameCount * (time_lived / p->totalLifespan)); - float shiftU = curFrame * (rec.U2 - rec.U1); - - rec.U1 += shiftU;/* * 0.0078125f; */ - rec.U2 += shiftU;/* * 0.0078125f; */ - - Vec3_Lerp(&pos, &p->base.lastPos, &p->base.nextPos, t); - size.x = p->base.size; size.y = size.x; - - x = Math_Floor(pos.x); y = Math_Floor(pos.y); z = Math_Floor(pos.z); - col = e->fullBright ? PACKEDCOL_WHITE : Lighting.Color(x, y, z); - col = PackedCol_Tint(col, e->tintCol); - - Particle_DoRender(&size, &pos, &rec, col, vertices); -} - -static void Custom_Render(float t) { - struct VertexTextured* data; - int i; - if (!custom_count) return; - - data = (struct VertexTextured*)Gfx_LockDynamicVb(particles_VB, - VERTEX_FORMAT_TEXTURED, custom_count * 4); - for (i = 0; i < custom_count; i++) { - CustomParticle_Render(&custom_particles[i], t, data); - data += 4; - } - - Gfx_BindTexture(particles_TexId); - Gfx_UnlockDynamicVb(particles_VB); - Gfx_DrawVb_IndexedTris(custom_count * 4); -} - -static void Custom_RemoveAt(int i) { - for (; i < custom_count - 1; i++) { - custom_particles[i] = custom_particles[i + 1]; - } - custom_count--; -} - -static void Custom_Tick(double delta) { - int i; - for (i = 0; i < custom_count; i++) { - if (CustomParticle_Tick(&custom_particles[i], delta)) { - Custom_RemoveAt(i); i--; - } - } -} - - -/*########################################################################################################################* -*--------------------------------------------------------Particles--------------------------------------------------------* -*#########################################################################################################################*/ -void Particles_Render(float t) { - if (!terrain_count && !rain_count && !custom_count) return; - - if (Gfx.LostContext) return; - if (!particles_VB) - particles_VB = Gfx_CreateDynamicVb(VERTEX_FORMAT_TEXTURED, PARTICLES_MAX * 4); - - Gfx_SetAlphaTest(true); - - Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); - Terrain_Render(t); - Rain_Render(t); - Custom_Render(t); - - Gfx_SetAlphaTest(false); -} - -static void Particles_Tick(struct ScheduledTask* task) { - double delta = task->interval; - Terrain_Tick(delta); - Rain_Tick(delta); - Custom_Tick(delta); -} - void Particles_BreakBlockEffect(IVec3 coords, BlockID old, BlockID now) { struct TerrainParticle* p; TextureLoc loc; @@ -456,8 +355,8 @@ void Particles_BreakBlockEffect(IVec3 coords, BlockID old, BlockID now) { /* gridOffset gives the centre of the cell on a grid */ #define CELL_CENTRE ((1.0f / GRID_SIZE) * 0.5f) - maxU2 = baseRec.U1 + maxU * uScale; - maxV2 = baseRec.V1 + maxV * vScale; + maxU2 = baseRec.u1 + maxU * uScale; + maxV2 = baseRec.v1 + maxV * vScale; for (x = 0; x < GRID_SIZE; x++) { for (y = 0; y < GRID_SIZE; y++) { for (z = 0; z < GRID_SIZE; z++) { @@ -476,12 +375,12 @@ void Particles_BreakBlockEffect(IVec3 coords, BlockID old, BlockID now) { p->base.velocity.z = CELL_CENTRE + (cellZ - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); rec = baseRec; - rec.U1 = baseRec.U1 + Random_Range(&rnd, minU, maxUsedU) * uScale; - rec.V1 = baseRec.V1 + Random_Range(&rnd, minV, maxUsedV) * vScale; - rec.U2 = rec.U1 + 4 * uScale; - rec.V2 = rec.V1 + 4 * vScale; - rec.U2 = min(rec.U2, maxU2) - 0.01f * uScale; - rec.V2 = min(rec.V2, maxV2) - 0.01f * vScale; + rec.u1 = baseRec.u1 + Random_Range(&rnd, minU, maxUsedU) * uScale; + rec.v1 = baseRec.v1 + Random_Range(&rnd, minV, maxUsedV) * vScale; + rec.u2 = rec.u1 + 4 * uScale; + rec.v2 = rec.v1 + 4 * vScale; + rec.u2 = min(rec.u2, maxU2) - 0.01f * uScale; + rec.v2 = min(rec.v2, maxV2) - 0.01f * vScale; Vec3_Add(&p->base.lastPos, &origin, &cell); p->base.nextPos = p->base.lastPos; @@ -497,27 +396,102 @@ void Particles_BreakBlockEffect(IVec3 coords, BlockID old, BlockID now) { } } -void Particles_RainSnowEffect(float x, float y, float z) { - struct Particle* p; - int i, type; - for (i = 0; i < 2; i++) { - if (rain_count == PARTICLES_MAX) Rain_RemoveAt(0); - p = &rain_Particles[rain_count++]; +/*########################################################################################################################* +*-------------------------------------------------------Custom particle---------------------------------------------------* +*#########################################################################################################################*/ +#ifdef CC_BUILD_NETWORKING +struct CustomParticle { + struct Particle base; + int effectId; + float totalLifespan; +}; - p->velocity.x = Random_Float(&rnd) * 0.8f - 0.4f; /* [-0.4, 0.4] */ - p->velocity.z = Random_Float(&rnd) * 0.8f - 0.4f; - p->velocity.y = Random_Float(&rnd) + 0.4f; +struct CustomParticleEffect Particles_CustomEffects[256]; +static struct CustomParticle custom_particles[PARTICLES_MAX]; +static int custom_count; +static cc_uint8 collideFlags; +#define EXPIRES_UPON_TOUCHING_GROUND (1 << 0) +#define SOLID_COLLIDES (1 << 1) +#define LIQUID_COLLIDES (1 << 2) +#define LEAF_COLLIDES (1 << 3) - p->lastPos.x = x + Random_Float(&rnd); /* [0.0, 1.0] */ - p->lastPos.y = y + Random_Float(&rnd) * 0.1f + 0.01f; - p->lastPos.z = z + Random_Float(&rnd); +static cc_bool CustomParticle_CanPass(BlockID block) { + cc_uint8 draw, collide; + + draw = Blocks.Draw[block]; + if (draw == DRAW_TRANSPARENT_THICK && !(collideFlags & LEAF_COLLIDES)) return true; - p->nextPos = p->lastPos; - p->lifetime = 40.0f; + collide = Blocks.Collide[block]; + if (collide == COLLIDE_SOLID && (collideFlags & SOLID_COLLIDES)) return false; + if (collide == COLLIDE_LIQUID && (collideFlags & LIQUID_COLLIDES)) return false; + return true; +} - type = Random_Next(&rnd, 30); - p->size = type >= 28 ? 2 : (type >= 25 ? 4 : 3); +static cc_bool CustomParticle_Tick(struct CustomParticle* p, float delta) { + struct CustomParticleEffect* e = &Particles_CustomEffects[p->effectId]; + hitTerrain = false; + collideFlags = e->collideFlags; + + return PhysicsTick(&p->base, e->gravity, CustomParticle_CanPass, delta) + || (hitTerrain && (e->collideFlags & EXPIRES_UPON_TOUCHING_GROUND)); +} + +static void CustomParticle_Render(struct CustomParticle* p, float t, struct VertexTextured* vertices) { + struct CustomParticleEffect* e = &Particles_CustomEffects[p->effectId]; + Vec3 pos; + Vec2 size; + PackedCol col; + TextureRec rec = e->rec; + int x, y, z; + + float time_lived = p->totalLifespan - p->base.lifetime; + int curFrame = Math_Floor(e->frameCount * (time_lived / p->totalLifespan)); + float shiftU = curFrame * (rec.u2 - rec.u1); + + rec.u1 += shiftU;/* * 0.0078125f; */ + rec.u2 += shiftU;/* * 0.0078125f; */ + + Vec3_Lerp(&pos, &p->base.lastPos, &p->base.nextPos, t); + size.x = p->base.size; size.y = size.x; + + x = Math_Floor(pos.x); y = Math_Floor(pos.y); z = Math_Floor(pos.z); + col = e->fullBright ? PACKEDCOL_WHITE : Lighting.Color(x, y, z); + col = PackedCol_Tint(col, e->tintCol); + + Particle_DoRender(&size, &pos, &rec, col, vertices); +} + +static void Custom_Render(float t) { + struct VertexTextured* data; + int i; + if (!custom_count) return; + + data = (struct VertexTextured*)Gfx_LockDynamicVb(particles_VB, + VERTEX_FORMAT_TEXTURED, custom_count * 4); + for (i = 0; i < custom_count; i++) { + CustomParticle_Render(&custom_particles[i], t, data); + data += 4; + } + + Gfx_BindTexture(particles_TexId); + Gfx_UnlockDynamicVb(particles_VB); + Gfx_DrawVb_IndexedTris(custom_count * 4); +} + +static void Custom_RemoveAt(int i) { + for (; i < custom_count - 1; i++) { + custom_particles[i] = custom_particles[i + 1]; + } + custom_count--; +} + +static void Custom_Tick(float delta) { + int i; + for (i = 0; i < custom_count; i++) { + if (CustomParticle_Tick(&custom_particles[i], delta)) { + Custom_RemoveAt(i); i--; + } } } @@ -568,6 +542,40 @@ void Particles_CustomEffect(int effectID, float x, float y, float z, float origi if (IntersectsBlock(&p->base, CustomParticle_CanPass)) custom_count--; } } +#else +static int custom_count; + +static void Custom_Render(float t) { } +static void Custom_Tick(float delta) { } +#endif + + +/*########################################################################################################################* +*--------------------------------------------------------Particles--------------------------------------------------------* +*#########################################################################################################################*/ +void Particles_Render(float t) { + if (!terrain_count && !rain_count && !custom_count) return; + + if (Gfx.LostContext) return; + if (!particles_VB) + particles_VB = Gfx_CreateDynamicVb(VERTEX_FORMAT_TEXTURED, PARTICLES_MAX * 4); + + Gfx_SetAlphaTest(true); + + Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); + Terrain_Render(t); + Rain_Render(t); + Custom_Render(t); + + Gfx_SetAlphaTest(false); +} + +static void Particles_Tick(struct ScheduledTask* task) { + float delta = task->interval; + Terrain_Tick(delta); + Rain_Tick(delta); + Custom_Tick(delta); +} /*########################################################################################################################* diff --git a/src/Picking.c b/src/Picking.c index c9b1b5b48..b2e1b8817 100644 --- a/src/Picking.c +++ b/src/Picking.c @@ -15,39 +15,39 @@ static void TestAxis(struct RayTracer* t, float dAxis, Face fAxis) { if (dAxis >= pickedPos_dist) return; pickedPos_dist = dAxis; - t->Closest = fAxis; + t->closest = fAxis; } static void SetAsValid(struct RayTracer* t) { - t->TranslatedPos = t->pos; - t->Valid = true; + t->translatedPos = t->pos; + t->valid = true; pickedPos_dist = MATH_LARGENUM; - TestAxis(t, t->Intersect.x - t->Min.x, FACE_XMIN); - TestAxis(t, t->Intersect.x - t->Max.x, FACE_XMAX); - TestAxis(t, t->Intersect.y - t->Min.y, FACE_YMIN); - TestAxis(t, t->Intersect.y - t->Max.y, FACE_YMAX); - TestAxis(t, t->Intersect.z - t->Min.z, FACE_ZMIN); - TestAxis(t, t->Intersect.z - t->Max.z, FACE_ZMAX); + TestAxis(t, t->intersect.x - t->Min.x, FACE_XMIN); + TestAxis(t, t->intersect.x - t->Max.x, FACE_XMAX); + TestAxis(t, t->intersect.y - t->Min.y, FACE_YMIN); + TestAxis(t, t->intersect.y - t->Max.y, FACE_YMAX); + TestAxis(t, t->intersect.z - t->Min.z, FACE_ZMIN); + TestAxis(t, t->intersect.z - t->Max.z, FACE_ZMAX); - switch (t->Closest) { - case FACE_XMIN: t->TranslatedPos.x--; break; - case FACE_XMAX: t->TranslatedPos.x++; break; - case FACE_ZMIN: t->TranslatedPos.z--; break; - case FACE_ZMAX: t->TranslatedPos.z++; break; - case FACE_YMIN: t->TranslatedPos.y--; break; - case FACE_YMAX: t->TranslatedPos.y++; break; + switch (t->closest) { + case FACE_XMIN: t->translatedPos.x--; break; + case FACE_XMAX: t->translatedPos.x++; break; + case FACE_ZMIN: t->translatedPos.z--; break; + case FACE_ZMAX: t->translatedPos.z++; break; + case FACE_YMIN: t->translatedPos.y--; break; + case FACE_YMAX: t->translatedPos.y++; break; } } void RayTracer_SetInvalid(struct RayTracer* t) { static const IVec3 pos = { -1, -1, -1 }; t->pos = pos; - t->TranslatedPos = pos; + t->translatedPos = pos; - t->Valid = false; + t->valid = false; t->block = BLOCK_AIR; - t->Closest = FACE_COUNT; + t->closest = FACE_COUNT; } static float RayTracer_Div(float a, float b) { @@ -83,9 +83,9 @@ void RayTracer_Init(struct RayTracer* t, const Vec3* origin, const Vec3* dir) { t->tMax.z = RayTracer_Div(cellBoundary.z - origin->z, dir->z); /* Boundary is a plane on the XY axis. */ /* Determine how far we must travel along the ray before we have crossed a gridcell. */ - t->tDelta.x = RayTracer_Div((float)t->step.x, dir->x); - t->tDelta.y = RayTracer_Div((float)t->step.y, dir->y); - t->tDelta.z = RayTracer_Div((float)t->step.z, dir->z); + t->tDelta.x = (float)t->step.x * t->invDir.x; + t->tDelta.y = (float)t->step.y * t->invDir.y; + t->tDelta.z = (float)t->step.z * t->invDir.z; } void RayTracer_Step(struct RayTracer* t) { @@ -211,11 +211,11 @@ static cc_bool ClipBlock(struct RayTracer* t) { if (!Intersection_RayIntersectsBox(t->origin, t->invDir, t->Min, t->Max, &t0, &t1)) return false; Vec3_Mul1(&scaledDir, &t->dir, t0); /* scaledDir = dir * t0 */ - Vec3_Add(&t->Intersect, &t->origin, &scaledDir); /* intersect = origin + scaledDir */ + Vec3_Add(&t->intersect, &t->origin, &scaledDir); /* intersect = origin + scaledDir */ /* Only pick the block if the block is precisely within reach distance. */ lenSq = Vec3_LengthSquared(&scaledDir); - reach = LocalPlayer_Instance.ReachDistance; + reach = Entities.CurPlayer->ReachDistance; if (lenSq <= reach * reach) { SetAsValid(t); @@ -239,7 +239,7 @@ static cc_bool ClipCamera(struct RayTracer* t) { Intersection_RayIntersectsBox(t->origin, t->invDir, t->Min, t->Max, &t0, &t1); Vec3_Mul1(&intersect, &t->dir, t0); /* intersect = dir * t0 */ - Vec3_Add(&t->Intersect, &t->origin, &intersect); /* intersect = origin + dir * t0 */ + Vec3_Add(&t->intersect, &t->origin, &intersect); /* intersect = origin + dir * t0 */ SetAsValid(t); return true; } @@ -251,11 +251,11 @@ void Picking_CalcPickedBlock(const Vec3* origin, const Vec3* dir, float reach, s } void Picking_ClipCameraPos(const Vec3* origin, const Vec3* dir, float reach, struct RayTracer* t) { - cc_bool noClip = (!Camera.Clipping || LocalPlayer_Instance.Hacks.Noclip) - && LocalPlayer_Instance.Hacks.CanNoclip; + cc_bool noClip = (!Camera.Clipping || Entities.CurPlayer->Hacks.Noclip) + && Entities.CurPlayer->Hacks.CanNoclip; if (noClip || !World.Loaded || !RayTrace(t, origin, dir, reach, ClipCamera)) { RayTracer_SetInvalid(t); - Vec3_Mul1(&t->Intersect, dir, reach); /* intersect = dir * reach */ - Vec3_Add(&t->Intersect, origin, &t->Intersect); /* intersect = origin + dir * reach */ + Vec3_Mul1(&t->intersect, dir, reach); /* intersect = dir * reach */ + Vec3_Add(&t->intersect, origin, &t->intersect); /* intersect = origin + dir * reach */ } } diff --git a/src/Picking.h b/src/Picking.h index 356348bef..6aa544a58 100644 --- a/src/Picking.h +++ b/src/Picking.h @@ -24,10 +24,10 @@ struct RayTracer { IVec3 step; Vec3 tMax, tDelta; /* Result only data */ - Vec3 Intersect; /* Coords at which the ray exactly intersected this block. */ - IVec3 TranslatedPos; /* Coords of the neighbouring block that is closest to the player */ - cc_bool Valid; /* Whether the ray tracer actually intersected with a block */ - Face Closest; /* Face of the intersected block that is closet to the player */ + Vec3 intersect; /* Coords at which the ray exactly intersected this block. */ + IVec3 translatedPos; /* Coords of the neighbouring block that is closest to the player */ + cc_bool valid; /* Whether the ray tracer actually intersected with a block */ + Face closest; /* Face of the intersected block that is closet to the player */ }; /* Marks the given ray tracer as having no result. */ diff --git a/src/Platform.h b/src/Platform.h index 412b59a72..19b2c1bc4 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -24,8 +24,8 @@ typedef int cc_file; /* Origin points for when seeking in a file. */ /* NOTE: These have same values as SEEK_SET/SEEK_CUR/SEEK_END, do not change them */ enum File_SeekFrom { FILE_SEEKFROM_BEGIN, FILE_SEEKFROM_CURRENT, FILE_SEEKFROM_END }; -/* Number of milliseconds since 01/01/0001 to start of unix time. */ -#define UNIX_EPOCH 62135596800000ULL +/* Number of seconds since 01/01/0001 to start of unix time. */ +#define UNIX_EPOCH_SECONDS 62135596800ULL extern const cc_result ReturnCode_FileShareViolation; extern const cc_result ReturnCode_FileNotFound; @@ -121,6 +121,9 @@ extern const cc_string DynamicLib_Ext; #define DYNAMICLIB_QUOTE(x) #x #define DynamicLib_Sym(sym) { DYNAMICLIB_QUOTE(sym), (void**)&_ ## sym } #define DynamicLib_Sym2(name, sym) { name, (void**)&_ ## sym } +#if defined CC_BUILD_OS2 +#define DynamicLib_SymC(sym) { DYNAMICLIB_QUOTE(_ ## sym), (void**)&_ ## sym } +#endif CC_API cc_result DynamicLib_Load(const cc_string* path, void** lib); /* OBSOLETE */ CC_API cc_result DynamicLib_Get(void* lib, const char* name, void** symbol); /* OBSOLETE */ @@ -163,8 +166,8 @@ void Platform_Log2(const char* format, const void* a1, const void* a2); void Platform_Log3(const char* format, const void* a1, const void* a2, const void* a3); void Platform_Log4(const char* format, const void* a1, const void* a2, const void* a3, const void* a4); -/* Returns the current UTC time, as number of milliseconds since 1/1/0001 */ -CC_API TimeMS DateTime_CurrentUTC_MS(void); +/* Returns the current UTC time, as number of seconds since 1/1/0001 */ +CC_API TimeMS DateTime_CurrentUTC(void); /* Returns the current local Time. */ CC_API void DateTime_CurrentLocal(struct DateTime* t); /* Takes a platform-specific stopwatch measurement. */ @@ -208,12 +211,9 @@ cc_result File_Length(cc_file file, cc_uint32* len); typedef void (*Thread_StartFunc)(void); /* Blocks the current thread for the given number of milliseconds. */ CC_API void Thread_Sleep(cc_uint32 milliseconds); -/* Initialises a new thread that will run the given function. */ -/* Because of backend differences, func must also be provided in Thread_Start2 */ -CC_API void* Thread_Create(Thread_StartFunc func); -/* Starts a new thread that runs the given function. */ +/* Initialises and starts a new thread that runs the given function. */ /* NOTE: Threads must either be detached or joined, otherwise data leaks. */ -CC_API void Thread_Start2(void* handle, Thread_StartFunc func); +CC_API void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name); /* Frees the platform specific persistent data associated with the thread. */ /* NOTE: Once a thread has been detached, Thread_Join can no longer be used. */ CC_API void Thread_Detach(void* handle); @@ -270,6 +270,8 @@ cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* m cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified); /* Attempts to close the given socket */ void Socket_Close(cc_socket s); +/* Attempts to write all data to the given socket, returning ERR_END_OF_STREAM if it could not */ +cc_result Socket_WriteAll(cc_socket socket, const cc_uint8* data, cc_uint32 count); #ifdef CC_BUILD_MOBILE void Platform_ShareScreenshot(const cc_string* filename); diff --git a/src/Platform_3DS.c b/src/Platform_3DS.c index cb1c173b8..87df96c08 100644 --- a/src/Platform_3DS.c +++ b/src/Platform_3DS.c @@ -48,25 +48,14 @@ unsigned int __stacksize__ = 256 * 1024; *------------------------------------------------------Logging/Time-------------------------------------------------------* *#########################################################################################################################*/ void Platform_Log(const char* msg, int len) { - //cc_file fd = -1; - // CITRA LOGGING - //cc_string path = String_Init(msg, len, len); - //File_Open(&fd, &path); - //if (fd > 0) File_Close(fd); - - write(STDOUT_FILENO, msg, len); - write(STDOUT_FILENO, "\n", 1); - // output to debug service (visible in Citra with log level set to "Debug.Emulated:Debug", or on console via remote gdb) svcOutputDebugString(msg, len); - svcOutputDebugString("\n", 1); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -223,13 +212,9 @@ static void Exec3DSThread(void* param) { ((Thread_StartFunc)param)(); } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { //TODO: Not quite correct, but eh - return threadCreate(Exec3DSThread, (void*)func, 256 * 1024, 0x3f, -2, false); -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - + *handle = threadCreate(Exec3DSThread, (void*)func, stackSize, 0x3f, -2, false); } void Thread_Detach(void* handle) { @@ -456,6 +441,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_Android.c b/src/Platform_Android.c index 98d330f60..e09918dea 100644 --- a/src/Platform_Android.c +++ b/src/Platform_Android.c @@ -252,8 +252,7 @@ static void JNICALL java_runGameAsync(JNIEnv* env, jobject instance) { Platform_LogConst("Running game async!"); /* The game must be run on a separate thread, as blocking the */ /* main UI thread will cause a 'App not responding..' messagebox */ - thread = Thread_Create(android_main); - Thread_Start2(thread, android_main); + Thread_Run(&thread, android_main, 1024 * 1024, "Game"); // TODO check stack size needed Thread_Detach(thread); } static const JNINativeMethod methods[] = { diff --git a/src/Platform_Dreamcast.c b/src/Platform_Dreamcast.c index b4e13bb70..0962842e7 100644 --- a/src/Platform_Dreamcast.c +++ b/src/Platform_Dreamcast.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "_PlatformConsole.h" KOS_INIT_FLAGS(INIT_DEFAULT | INIT_NET); @@ -65,16 +66,16 @@ static void LogOnscreen(const char* msg, int len) { } void Platform_Log(const char* msg, int len) { - fs_write(STDOUT_FILENO, msg, len); - fs_write(STDOUT_FILENO, "\n", 1); + dbgio_write_buffer_xlat(msg, len); + dbgio_write_buffer_xlat("\n", 1); if (window_inited) return; // Log details on-screen for initial model initing etc - // (this can take around 40 seconds on average) + // (this can take around 40 seconds on average) LogOnscreen(msg, len); } -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { uint32 secs, ms; timer_ms_gettime(&secs, &ms); @@ -85,7 +86,7 @@ TimeMS DateTime_CurrentUTC_MS(void) { if (boot_time < boot_time_2000) boot_time = boot_time_2024; cc_uint64 curSecs = boot_time + secs; - return (curSecs * 1000 + ms) + UNIX_EPOCH; + return curSecs + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -256,14 +257,11 @@ static void* ExecThread(void* param) { return NULL; } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { kthread_attr_t attrs = { 0 }; - attrs.stack_size = 96 * 1024; - attrs.label = "CC thread"; - return thd_create_ex(&attrs, ExecThread, func); -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { + attrs.stack_size = stackSize; + attrs.label = name; + *handle = thd_create_ex(&attrs, ExecThread, func); } void Thread_Detach(void* handle) { @@ -448,6 +446,9 @@ static uint8 partition_type; static void InitSDCard(void) { if (sd_init()) { + // Both SD card and debug interface use the serial port + // So if initing SD card fails, need to restore serial port state for debug logging + scif_init(); Platform_LogConst("Failed to init SD card"); return; } @@ -522,6 +523,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* @@ -529,4 +535,4 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { static cc_result GetMachineID(cc_uint32* key) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Platform_GCWii.c b/src/Platform_GCWii.c index 7a003697d..9f660cd84 100644 --- a/src/Platform_GCWii.c +++ b/src/Platform_GCWii.c @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef HW_RVL #include #endif @@ -42,30 +43,43 @@ const char* Platform_AppNameSuffix = " GameCube"; /*########################################################################################################################* *------------------------------------------------------Logging/Time-------------------------------------------------------* *#########################################################################################################################*/ -// dolphin recognises this function name (if loaded as .elf), and will patch it -// to also log the message to dolphin's console at OSREPORT-HLE log level -void CC_NOINLINE __write_console(int fd, const char* msg, const u32* size) { - write(STDOUT_FILENO, msg, *size); // this can be intercepted by libogc debug console -} -void Platform_Log(const char* msg, int len) { - char buffer[256]; - cc_string str = String_Init(buffer, 0, 254); // 2 characters (\n and \0) - u32 size; - - String_AppendAll(&str, msg, len); - buffer[str.length + 0] = '\n'; - buffer[str.length + 1] = '\0'; // needed to make Dolphin logger happy - - size = str.length + 1; // +1 for '\n' - __write_console(0, buffer, &size); - // TODO: Just use printf("%s", somehow ??? +// To see these log messages: +// 1) In the UI, make sure 'Show log configuration' checkbox is checked in View menu +// 2) Make sure "OSReport EXI (OSREPORT)" log type is enabled +// 3) In the UI, make sure 'Show log' checkbox is checked in View menu +static void LogOverEXI(char* msg, int len) { + u32 cmd = 0x80000000 | (0x800400 << 6); // write flag, UART base address + + // https://hitmen.c02.at/files/yagcd/yagcd/chap10.html + // Try to acquire "MASK ROM"/"IPL" link + // Writing to the IPL is used for debug message logging + if (EXI_Lock(EXI_CHANNEL_0, EXI_DEVICE_1, NULL) == 0) return; + if (EXI_Select(EXI_CHANNEL_0, EXI_DEVICE_1, EXI_SPEED8MHZ) == 0) { + EXI_Unlock(EXI_CHANNEL_0); return; + } + + EXI_Imm( EXI_CHANNEL_0, &cmd, 4, EXI_WRITE, NULL); + EXI_Sync( EXI_CHANNEL_0); + EXI_ImmEx( EXI_CHANNEL_0, msg, len, EXI_WRITE); + EXI_Deselect(EXI_CHANNEL_0); + EXI_Unlock( EXI_CHANNEL_0); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { - struct timeval cur; - gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); +void Platform_Log(const char* msg, int len) { + char tmp[256 + 1]; + len = min(len, 256); + // See EXI_DeviceIPL.cpp in Dolphin, \r is what triggers buffered message to be logged + Mem_Copy(tmp, msg, len); tmp[len] = '\r'; + + LogOverEXI(tmp, len + 1); +} + +#define GCWII_EPOCH_ADJUST 946684800ULL // GameCube/Wii time epoch is year 2000, not 1970 + +TimeMS DateTime_CurrentUTC(void) { + u64 raw = gettime(); + u64 secs = ticks_to_secs(raw); + return secs + UNIX_EPOCH_SECONDS + GCWII_EPOCH_ADJUST; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -175,8 +189,6 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall } static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { - if (!fat_available) return ENOSYS; - char str[NATIVE_STR_LEN]; GetNativePath(str, path); *file = open(str, mode, 0); @@ -184,12 +196,17 @@ static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { } cc_result File_Open(cc_file* file, const cc_string* path) { + if (!fat_available) return ReturnCode_FileNotFound; return File_Do(file, path, O_RDONLY); } + cc_result File_Create(cc_file* file, const cc_string* path) { + if (!fat_available) return ENOTSUP; return File_Do(file, path, O_RDWR | O_CREAT | O_TRUNC); } + cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { + if (!fat_available) return ENOTSUP; return File_Do(file, path, O_RDWR | O_CREAT); } @@ -234,13 +251,11 @@ static void* ExecThread(void* param) { return NULL; } -void* Thread_Create(Thread_StartFunc func) { - return Mem_Alloc(1, sizeof(lwp_t), "thread"); -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - lwp_t* ptr = (lwp_t*)handle; - int res = LWP_CreateThread(ptr, ExecThread, (void*)func, NULL, 256 * 1024, 80); +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + lwp_t* thread = (lwp_t*)Mem_Alloc(1, sizeof(lwp_t), "thread"); + *handle = thread; + + int res = LWP_CreateThread(thread, ExecThread, (void*)func, NULL, stackSize, 80); if (res) Logger_Abort2(res, "Creating thread"); } @@ -580,6 +595,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* @@ -596,4 +616,4 @@ static cc_result GetMachineID(cc_uint32* key) { } #endif -#endif \ No newline at end of file +#endif diff --git a/src/Platform_N64.c b/src/Platform_N64.c index 7c57cedde..81cf6b52a 100644 --- a/src/Platform_N64.c +++ b/src/Platform_N64.c @@ -48,11 +48,8 @@ void Platform_Log(const char* msg, int len) { write(STDERR_FILENO, "\n", 1); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { - struct timeval cur; - gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); +TimeMS DateTime_CurrentUTC(void) { + return 0; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -173,12 +170,8 @@ void Thread_Sleep(cc_uint32 milliseconds) { wait_ms(milliseconds); } -void* Thread_Create(Thread_StartFunc func) { - return NULL; -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - // TODO: actual multithreading ??? +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + *handle = NULL; } void Thread_Detach(void* handle) { @@ -267,13 +260,6 @@ void Platform_Init(void) { DisableFpuExceptions(); Platform_ReadonlyFilesystem = true; - // TODO: Redesign Drawer2D to better handle this - Options_SetBool(OPT_USE_CHAT_FONT, true); - - //console_init(); - //console_set_render_mode(RENDER_AUTOMATIC); - //console_set_debug(true); - dfs_init(DFS_DEFAULT_LOCATION); timer_init(); rtc_init(); @@ -297,6 +283,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_NDS.c b/src/Platform_NDS.c index af95c81ef..b0194b3cd 100644 --- a/src/Platform_NDS.c +++ b/src/Platform_NDS.c @@ -8,15 +8,28 @@ #include "Utils.h" #include "Errors.h" #include "Options.h" +#include "Animations.h" #include #include #include +#include #include #include #include #include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "_PlatformConsole.h" const cc_result ReturnCode_FileShareViolation = 1000000000; // not used @@ -25,42 +38,53 @@ const cc_result ReturnCode_SocketInProgess = EINPROGRESS; const cc_result ReturnCode_SocketWouldBlock = EWOULDBLOCK; const cc_result ReturnCode_DirectoryExists = EEXIST; const char* Platform_AppNameSuffix = " NDS"; +extern cc_bool keyboardOpen; /*########################################################################################################################* *------------------------------------------------------Logging/Time-------------------------------------------------------* *#########################################################################################################################*/ -static u32 rtcSecs; -static void syncRTC(void) { rtcSecs++; } +static u32 last_raw; +static u64 base_time; cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { if (end < beg) return 0; - - return end - beg; + + return timerTicks2usec(end - beg); } cc_uint64 Stopwatch_Measure(void) { - u16 raw = timerTick(1); - u32 usecs = timerTicks2usec(raw); - return rtcSecs * 1000000ULL + usecs; + u32 raw = cpuGetTiming(); + // Since counter is only a 32 bit integer, it overflows after a minute or two + if (last_raw > 0xF0000000 && raw < 0x10000000) { + base_time += 0x100000000ULL; + } + + last_raw = raw; + return base_time + raw; } +static void LogNocash(const char* msg, int len) { + // Can only be up to 120 bytes total + char buffer[120]; + len = min(len, 118); + + Mem_Copy(buffer, msg, len); + buffer[len + 0] = '\n'; + buffer[len + 1] = '\0'; + nocashWrite(buffer, len + 2); +} + +extern void consolePrintString(const char* ptr, int len); void Platform_Log(const char* msg, int len) { - char buffer[256]; - cc_string str; - String_InitArray(str, buffer); - - String_AppendAll(&str, msg, len); - buffer[str.length] = '\0'; - - printf("%s\n", buffer); + LogNocash(msg, len); + consolePrintString(msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -81,69 +105,124 @@ void DateTime_CurrentLocal(struct DateTime* t) { /*########################################################################################################################* *-----------------------------------------------------Directory/File------------------------------------------------------* *#########################################################################################################################*/ -static const cc_string root_path = String_FromConst("/"); +static cc_string root_path = String_FromConst("fat:/"); // may be overriden in InitFilesystem +static bool fat_available; static void GetNativePath(char* str, const cc_string* path) { - // TODO temp hack - cc_string path_ = *path; - int idx = String_IndexOf(path, '/'); - if (idx >= 0) path_ = String_UNSAFE_SubstringAt(&path_, idx + 1); - Mem_Copy(str, root_path.buffer, root_path.length); str += root_path.length; - String_EncodeUtf8(str, &path_); + String_EncodeUtf8(str, path); } cc_result Directory_Create(const cc_string* path) { - return ERR_NOT_SUPPORTED; + if (!fat_available) return 0; + + char str[NATIVE_STR_LEN]; + GetNativePath(str, path); + Platform_Log1("mkdir %c", str); + + return mkdir(str, 0) == -1 ? errno : 0; } int File_Exists(const cc_string* path) { - return false; + if (!fat_available) return false; + + char str[NATIVE_STR_LEN]; + struct stat sb; + GetNativePath(str, path); + Platform_Log1("Check %c", str); + + return stat(str, &sb) == 0 && S_ISREG(sb.st_mode); } cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { return ERR_NOT_SUPPORTED; } +static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { + char str[NATIVE_STR_LEN]; + GetNativePath(str, path); + Platform_Log1("Open %c", str); + + *file = open(str, mode, 0); + return *file == -1 ? errno : 0; +} + cc_result File_Open(cc_file* file, const cc_string* path) { - *file = -1; - return ERR_NOT_SUPPORTED; + if (!fat_available) return ReturnCode_FileNotFound; + return File_Do(file, path, O_RDONLY); } cc_result File_Create(cc_file* file, const cc_string* path) { - *file = -1; - return ERR_NOT_SUPPORTED; + if (!fat_available) return ENOTSUP; + return File_Do(file, path, O_RDWR | O_CREAT | O_TRUNC); } cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { - *file = -1; - return ERR_NOT_SUPPORTED; + if (!fat_available) return ENOTSUP; + return File_Do(file, path, O_RDWR | O_CREAT); } cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) { - return ERR_NOT_SUPPORTED; + *bytesRead = read(file, data, count); + return *bytesRead == -1 ? errno : 0; } cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* bytesWrote) { - return ERR_NOT_SUPPORTED; + *bytesWrote = write(file, data, count); + return *bytesWrote == -1 ? errno : 0; } cc_result File_Close(cc_file file) { - if (file < 0) return 0; - return ERR_NOT_SUPPORTED; + return close(file) == -1 ? errno : 0; } cc_result File_Seek(cc_file file, int offset, int seekType) { - return ERR_NOT_SUPPORTED; + static cc_uint8 modes[3] = { SEEK_SET, SEEK_CUR, SEEK_END }; + return lseek(file, offset, modes[seekType]) == -1 ? errno : 0; } cc_result File_Position(cc_file file, cc_uint32* pos) { - return ERR_NOT_SUPPORTED; + *pos = lseek(file, 0, SEEK_CUR); + return *pos == -1 ? errno : 0; } cc_result File_Length(cc_file file, cc_uint32* len) { - return ERR_NOT_SUPPORTED; + struct stat st; + if (fstat(file, &st) == -1) { *len = -1; return errno; } + *len = st.st_size; return 0; +} + +static int LoadFatFilesystem(void* arg) { + fat_available = fatInitDefault(); + return 0; +} + +static void InitFilesystem(void) { + cothread_t thread = cothread_create(LoadFatFilesystem, NULL, 0, 0); + // If running with DSi mode in melonDS and the internal SD card is enabled, then + // fatInitDefault gets stuck in sdmmc_ReadSectors - because the fifoWaitValue32Async will never return + // (You can tell when this happens - "MMC: unknown CMD 17 00000000" is logged to console) + // However, since it does yield to cothreads, workaround this by running fatInitDefault on another thread + // and then giving up if it takes too long.. not the most elegant solution, but it does work + if (thread == -1) { + LoadFatFilesystem(NULL); + } else { + for (int i = 0; i < 100; i++) + { + cothread_yield(); + if (cothread_has_joined(thread)) break; + + swiDelay(2000); + } + } + + char* dir = fatGetDefaultCwd(); + if (dir) { + root_path.buffer = dir; + root_path.length = String_Length(dir); + } + Platform_ReadonlyFilesystem = !fat_available; } @@ -155,12 +234,8 @@ void Thread_Sleep(cc_uint32 milliseconds) { swiDelay(8378 * milliseconds); // TODO probably wrong } -void* Thread_Create(Thread_StartFunc func) { - return NULL; -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - // TODO: actual multithreading ??? +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + *handle = NULL; } void Thread_Detach(void* handle) { @@ -202,30 +277,143 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { /*########################################################################################################################* *---------------------------------------------------------Socket----------------------------------------------------------* *#########################################################################################################################*/ +static cc_bool net_supported = true; + +static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { + struct hostent* res = gethostbyname(host); + struct sockaddr_in* addr4; + char* src_addr; + int i; + + // avoid confusion with SSL error codes + // e.g. FFFF FFF7 > FF00 FFF7 + if (!res) return -0xFF0000 + errno; + + // Must have at least one IPv4 address + if (res->h_addrtype != AF_INET) return ERR_INVALID_ARGUMENT; + if (!res->h_addr_list) return ERR_INVALID_ARGUMENT; + + for (i = 0; i < SOCKET_MAX_ADDRS; i++) + { + src_addr = res->h_addr_list[i]; + if (!src_addr) break; + addrs[i].size = sizeof(struct sockaddr_in); + + addr4 = (struct sockaddr_in*)addrs[i].data; + addr4->sin_family = AF_INET; + addr4->sin_port = htons(port); + addr4->sin_addr = *(struct in_addr*)src_addr; + } + + *numValidAddrs = i; + return i == 0 ? ERR_INVALID_ARGUMENT : 0; +} + cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) { - return ERR_NOT_SUPPORTED; + struct sockaddr_in* addr4 = (struct sockaddr_in*)addrs[0].data; + char str[NATIVE_STR_LEN]; + String_EncodeUtf8(str, address); + + if (!net_supported) return ERR_NOT_SUPPORTED; + *numValidAddrs = 1; + + if (inet_aton(str, &addr4->sin_addr) > 0) { + addr4->sin_family = AF_INET; + addr4->sin_port = htons(port); + + addrs[0].size = sizeof(*addr4); + return 0; + } + + return ParseHost(str, port, addrs, numValidAddrs); } cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { - return ERR_NOT_SUPPORTED; + struct sockaddr* raw = (struct sockaddr*)addr->data; + int res; + if (!net_supported) { *s = -1; return ERR_NOT_SUPPORTED; } + + *s = socket(raw->sa_family, SOCK_STREAM, 0); + if (*s < 0) return errno; + + if (nonblocking) { + int blocking_raw = 1; /* non-blocking mode */ + ioctl(*s, FIONBIO, &blocking_raw); + } + + res = connect(*s, raw, addr->size); + return res < 0 ? errno : 0; } cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { - return ERR_NOT_SUPPORTED; + int res = recv(s, data, count, 0); + if (res < 0) { *modified = 0; return errno; } + + *modified = res; return 0; } cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { - return ERR_NOT_SUPPORTED; + int res = send(s, data, count, 0); + if (res < 0) { *modified = 0; return errno; } + + *modified = res; return 0; } -void Socket_Close(cc_socket s) { } +void Socket_Close(cc_socket s) { + shutdown(s, 2); // SHUT_RDWR = 2 + closesocket(s); +} + +// libogc only implements net_select for gamecube currently +static cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) { + fd_set set; + struct timeval time = { 0 }; + int res; // number of 'ready' sockets + FD_ZERO(&set); + FD_SET(s, &set); + if (mode == SOCKET_POLL_READ) { + res = select(s + 1, &set, NULL, NULL, &time); + } else { + res = select(s + 1, NULL, &set, NULL, &time); + } + if (res < 0) { *success = false; return errno; } + *success = FD_ISSET(s, &set) != 0; return 0; +} cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable) { - return ERR_NOT_SUPPORTED; + return Socket_Poll(s, SOCKET_POLL_READ, readable); } cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { - return ERR_NOT_SUPPORTED; + int resultSize = sizeof(int); + cc_result res = Socket_Poll(s, SOCKET_POLL_WRITE, writable); + if (res || *writable) return res; + + /* https://stackoverflow.com/questions/29479953/so-error-value-after-successful-socket-operation */ + getsockopt(s, SOL_SOCKET, SO_ERROR, &res, &resultSize); + return res; +} + +static void InitNetworking(void) { + if (!Wifi_InitDefault(INIT_ONLY)) { + Platform_LogConst("Initing WIFI failed"); + net_supported = false; return; + } + Wifi_AutoConnect(); + + for (int i = 0; i < 300; i++) + { + int status = Wifi_AssocStatus(); + if (status == ASSOCSTATUS_ASSOCIATED) return; + + if (status == ASSOCSTATUS_CANNOTCONNECT) { + Platform_LogConst("Can't connect to WIFI"); + net_supported = false; return; + } + swiWaitForVBlank(); + } + Platform_LogConst("Gave up after 300 tries"); + net_supported = false; } @@ -233,11 +421,10 @@ cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { *--------------------------------------------------------Platform---------------------------------------------------------* *#########################################################################################################################*/ void Platform_Init(void) { - // Setup a timer that triggers an interrupt once per second - timerStart(1, ClockDivider_1024, TIMER_FREQ_1024(1), syncRTC); - - // TODO: Redesign Drawer2D to better handle this - Options_SetBool(OPT_USE_CHAT_FONT, true); + InitFilesystem(); + InitNetworking(); + + cpuStartTiming(1); } void Platform_Free(void) { } @@ -258,6 +445,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_PS1.c b/src/Platform_PS1.c new file mode 100644 index 000000000..f4ad6f5a4 --- /dev/null +++ b/src/Platform_PS1.c @@ -0,0 +1,250 @@ +#include "Core.h" +#if defined PLAT_PS1 + +#include "_PlatformBase.h" +#include "Stream.h" +#include "ExtMath.h" +#include "Funcs.h" +#include "Window.h" +#include "Utils.h" +#include "Errors.h" +#include "Options.h" +#include "PackedCol.h" +#include +#include +#include +#include +#include +#include +#include +void exit(int code) { _boot(); } + +// The SDK calloc doesn't zero memory, so need to override it +void* calloc(size_t num, size_t size) { + void* ptr = malloc(num * size); + if (ptr) memset(ptr, 0, num * size); + return ptr; +} +#include "_PlatformConsole.h" + +const cc_result ReturnCode_FileShareViolation = 1000000000; // not used +const cc_result ReturnCode_FileNotFound = 99999; +const cc_result ReturnCode_DirectoryExists = 99999; + +const cc_result ReturnCode_SocketInProgess = -1; +const cc_result ReturnCode_SocketWouldBlock = -1; +const char* Platform_AppNameSuffix = " PS1"; + + +/*########################################################################################################################* +*------------------------------------------------------Logging/Time-------------------------------------------------------* +*#########################################################################################################################*/ +void Platform_Log(const char* msg, int len) { + char tmp[2048 + 1]; + len = min(len, 2048); + Mem_Copy(tmp, msg, len); tmp[len] = '\0'; + + printf("%s\n", tmp); +} + +TimeMS DateTime_CurrentUTC(void) { + return 0; +} + +void DateTime_CurrentLocal(struct DateTime* t) { + Mem_Set(t, 0, sizeof(struct DateTime)); +} + + +/*########################################################################################################################* +*--------------------------------------------------------Stopwatch--------------------------------------------------------* +*#########################################################################################################################*/ +static volatile cc_uint32 irq_count; + +cc_uint64 Stopwatch_Measure(void) { + return irq_count; +} + +cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { + if (end < beg) return 0; + return (end - beg) * 1000; +} + +static void timer2_handler(void) { irq_count++; } + +static void Stopwatch_Init(void) { + TIMER_CTRL(2) = 0x0258; // CLK/8 input, IRQ on reload + TIMER_RELOAD(2) = (F_CPU / 8) / 1000; // 1000 Hz + + EnterCriticalSection(); + InterruptCallback(IRQ_TIMER2, &timer2_handler); + ExitCriticalSection(); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Directory/File------------------------------------------------------* +*#########################################################################################################################*/ +static const cc_string root_path = String_FromConst("cdrom:/"); + +static void GetNativePath(char* str, const cc_string* path) { + Mem_Copy(str, root_path.buffer, root_path.length); + str += root_path.length; + String_EncodeUtf8(str, path); +} + +cc_result Directory_Create(const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +int File_Exists(const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Open(cc_file* file, const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Create(cc_file* file, const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* bytesWrote) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Close(cc_file file) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Seek(cc_file file, int offset, int seekType) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Position(cc_file file, cc_uint32* pos) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Length(cc_file file, cc_uint32* len) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Threading--------------------------------------------------------* +*#########################################################################################################################*/ +void Thread_Sleep(cc_uint32 milliseconds) { + // TODO sleep a bit + VSync(0); +} + +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + *handle = NULL; +} + +void Thread_Detach(void* handle) { +} + +void Thread_Join(void* handle) { +} + +void* Mutex_Create(void) { + return NULL; +} + +void Mutex_Free(void* handle) { +} + +void Mutex_Lock(void* handle) { +} + +void Mutex_Unlock(void* handle) { +} + +void* Waitable_Create(void) { + return NULL; +} + +void Waitable_Free(void* handle) { +} + +void Waitable_Signal(void* handle) { +} + +void Waitable_Wait(void* handle) { +} + +void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { +} + + +/*########################################################################################################################* +*---------------------------------------------------------Socket----------------------------------------------------------* +*#########################################################################################################################*/ +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + return ERR_NOT_SUPPORTED; +} + +void Socket_Close(cc_socket s) { +} + +cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Platform---------------------------------------------------------* +*#########################################################################################################################*/ +void Platform_Init(void) { + ResetGraph(0); + Stopwatch_Init(); +} + +void Platform_Free(void) { } + +cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*-------------------------------------------------------Encryption--------------------------------------------------------* +*#########################################################################################################################*/ +static cc_result GetMachineID(cc_uint32* key) { + return ERR_NOT_SUPPORTED; +} +#endif diff --git a/src/Platform_PS2.c b/src/Platform_PS2.c index 0bfe63c34..8adb545a3 100644 --- a/src/Platform_PS2.c +++ b/src/Platform_PS2.c @@ -30,6 +30,7 @@ #include #include #include +#include #define NEWLIB_PORT_AWARE #include #include @@ -37,8 +38,8 @@ #include "_PlatformConsole.h" const cc_result ReturnCode_FileShareViolation = 1000000000; // not used -const cc_result ReturnCode_FileNotFound = -ENOENT; -const cc_result ReturnCode_DirectoryExists = -EEXIST; +const cc_result ReturnCode_FileNotFound = -4; +const cc_result ReturnCode_DirectoryExists = -8; const cc_result ReturnCode_SocketInProgess = EINPROGRESS; const cc_result ReturnCode_SocketWouldBlock = EWOULDBLOCK; @@ -56,11 +57,10 @@ void Platform_Log(const char* msg, int len) { _print("%s", tmp); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -225,8 +225,6 @@ cc_result File_Length(cc_file file, cc_uint32* len) { /*########################################################################################################################* *--------------------------------------------------------Threading--------------------------------------------------------* *#########################################################################################################################*/ -#define STACK_SIZE (128 * 1024) - void Thread_Sleep(cc_uint32 milliseconds) { DelayThread(milliseconds * 1000); } @@ -238,28 +236,24 @@ static int ExecThread(void* param) { ee_thread_status_t info; int res = ReferThreadStatus(thdID, &info); - if (res > 0 && info.stack) Mem_Free(info.stack); + if (res > 0 && info.stack) Mem_Free(info.stack); // TODO is it okay to free stack of running thread ???? return 0; // TODO detach ? } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { ee_thread_t thread = { 0 }; thread.func = ExecThread; - thread.stack = Mem_Alloc(STACK_SIZE, 1, "Thread stack"); - thread.stack_size = STACK_SIZE; + thread.stack = Mem_Alloc(stackSize, 1, "Thread stack"); + thread.stack_size = stackSize; thread.gp_reg = &_gp; thread.initial_priority = 18; int thdID = CreateThread(&thread); if (thdID < 0) Logger_Abort2(thdID, "Creating thread"); - return (void*)thdID; -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - int thdID = (int)handle; - int res = StartThread(thdID, (void*)func); + *handle = thdID; + int res = StartThread(thdID, (void*)func); if (res < 0) Logger_Abort2(res, "Running thread"); } @@ -712,6 +706,9 @@ void Platform_Init(void) { // Create root directory int res = fioMkdir("mass:/ClassiCube"); Platform_Log1("ROOT CREATE %i", &res); + + dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); + dma_channel_fast_waits(DMA_CHANNEL_GIF); } void Platform_Free(void) { } @@ -733,6 +730,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_PS3.c b/src/Platform_PS3.c index 6fddd2265..b4c15a258 100644 --- a/src/Platform_PS3.c +++ b/src/Platform_PS3.c @@ -52,11 +52,10 @@ void Platform_Log(const char* msg, int len) { sysTtyWrite(STDOUT_FILENO, "\n", 1, &done); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { - struct timeval cur; - gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); +TimeMS DateTime_CurrentUTC(void) { + u64 sec, nanosec; + sysGetCurrentTime(&sec, &nanosec); + return sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -236,16 +235,13 @@ void Thread_Sleep(cc_uint32 milliseconds) { static void ExecThread(void* param) { ((Thread_StartFunc)param)(); } -#define STACK_SIZE (128 * 1024) -void* Thread_Create(Thread_StartFunc func) { - return Mem_Alloc(1, sizeof(sys_ppu_thread_t), "thread"); -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - sys_ppu_thread_t* thread = (sys_ppu_thread_t*)handle; +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + sys_ppu_thread_t* thread = (sys_ppu_thread_t*)Mem_Alloc(1, sizeof(sys_ppu_thread_t), "thread"); + *handle = thread; + int res = sysThreadCreate(thread, ExecThread, (void*)func, - 0, STACK_SIZE, THREAD_JOINABLE, "CC thread"); + 0, stackSize, THREAD_JOINABLE, name); if (res) Logger_Abort2(res, "Creating thread"); } @@ -479,6 +475,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* @@ -486,4 +487,4 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { static cc_result GetMachineID(cc_uint32* key) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Platform_PSP.c b/src/Platform_PSP.c index 4cd3fef71..32fa8f4c0 100644 --- a/src/Platform_PSP.c +++ b/src/Platform_PSP.c @@ -54,11 +54,10 @@ void Platform_Log(const char* msg, int len) { //File_Close(file); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct SceKernelTimeval cur; sceKernelLibcGettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -85,11 +84,12 @@ cc_uint64 Stopwatch_Measure(void) { /*########################################################################################################################* *-----------------------------------------------------Directory/File------------------------------------------------------* *#########################################################################################################################*/ -extern int __path_absolute(const char *in, char *out, int len); +static const cc_string root_path = String_FromConst("ms0:/PSP/GAME/ClassiCube/"); + static void GetNativePath(char* str, const cc_string* path) { - char tmp[NATIVE_STR_LEN + 1]; - String_EncodeUtf8(tmp, path); - __path_absolute(tmp, str, NATIVE_STR_LEN); + Mem_Copy(str, root_path.buffer, root_path.length); + str += root_path.length; + String_EncodeUtf8(str, path); } #define GetSCEResult(result) (result >= 0 ? 0 : result & 0xFFFF) @@ -218,18 +218,16 @@ static int ExecThread(unsigned int argc, void *argv) { return 0; } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { #define CC_THREAD_PRIORITY 17 // TODO: 18? - #define CC_THREAD_STACKSIZE 128 * 1024 #define CC_THREAD_ATTRS 0 // TODO PSP_THREAD_ATTR_VFPU? - - return (void*)sceKernelCreateThread("CC thread", ExecThread, CC_THREAD_PRIORITY, - CC_THREAD_STACKSIZE, CC_THREAD_ATTRS, NULL); -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { Thread_StartFunc func_ = func; - sceKernelStartThread((int)handle, sizeof(func_), (void*)&func_); + + int threadID = sceKernelCreateThread(name, ExecThread, CC_THREAD_PRIORITY, + stackSize, CC_THREAD_ATTRS, NULL); + + *handle = (int)threadID; + sceKernelStartThread(threadID, sizeof(func_), (void*)&func_); } void Thread_Detach(void* handle) { @@ -440,6 +438,9 @@ void Platform_Init(void) { // *tx = vel->x == 0.0f ? MATH_LARGENUM : Math_AbsF(dx / vel->x); // TODO: work out why this error is actually happening (inexact or underflow?) and properly fix it pspSdkDisableFPUExceptions(); + + // Create root directory + Directory_Create(&String_Empty); } void Platform_Free(void) { } @@ -460,6 +461,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_PSVita.c b/src/Platform_PSVita.c index 05d476689..afefa9210 100644 --- a/src/Platform_PSVita.c +++ b/src/Platform_PSVita.c @@ -37,11 +37,10 @@ void Platform_Log(const char* msg, int len) { sceIoWrite(stdout_fd, msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.sec * 1000 + UNIX_EPOCH + (time.usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct SceKernelTimeval cur; sceKernelLibcGettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -202,18 +201,16 @@ static int ExecThread(unsigned int argc, void *argv) { return 0; } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { #define CC_THREAD_PRIORITY 0x10000100 - #define CC_THREAD_STACKSIZE 128 * 1024 #define CC_THREAD_ATTRS 0 // TODO PSP_THREAD_ATTR_VFPU? - - return (void*)sceKernelCreateThread("CC thread", ExecThread, CC_THREAD_PRIORITY, - CC_THREAD_STACKSIZE, CC_THREAD_ATTRS, 0, NULL); -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { Thread_StartFunc func_ = func; - sceKernelStartThread((int)handle, sizeof(func_), (void*)&func_); + + int threadID = sceKernelCreateThread(name, ExecThread, CC_THREAD_PRIORITY, + stackSize, CC_THREAD_ATTRS, 0, NULL); + + *handle = (int)threadID; + sceKernelStartThread(threadID, sizeof(func_), (void*)&func_); } void Thread_Detach(void* handle) { @@ -420,6 +417,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return true; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_Posix.c b/src/Platform_Posix.c index 2e9db9259..5a95592ee 100644 --- a/src/Platform_Posix.c +++ b/src/Platform_Posix.c @@ -61,6 +61,12 @@ cc_bool Platform_SingleProcess; /* TODO: Use load_image/resume_thread instead of fork */ /* Otherwise opening browser never works because fork fails */ #include +#elif defined CC_BUILD_OS2 +#include +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_PM +#include #endif @@ -103,15 +109,14 @@ void Platform_Log(const char* msg, int len) { } #endif -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { - struct timeval cur; + struct timeval cur; struct tm loc_time; gettimeofday(&cur, NULL); localtime_r(&cur.tv_sec, &loc_time); @@ -264,18 +269,31 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { char str[NATIVE_STR_LEN]; String_EncodeUtf8(str, path); + *file = open(str, mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); return *file == -1 ? errno : 0; } cc_result File_Open(cc_file* file, const cc_string* path) { +#if !defined CC_BUILD_OS2 return File_Do(file, path, O_RDONLY); +#else + return File_Do(file, path, O_RDONLY | O_BINARY); +#endif } cc_result File_Create(cc_file* file, const cc_string* path) { +#if !defined CC_BUILD_OS2 return File_Do(file, path, O_RDWR | O_CREAT | O_TRUNC); +#else + return File_Do(file, path, O_RDWR | O_CREAT | O_TRUNC | O_BINARY); +#endif } cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { +#if !defined CC_BUILD_OS2 return File_Do(file, path, O_RDWR | O_CREAT); +#else + return File_Do(file, path, O_RDWR | O_CREAT | O_BINARY); +#endif } cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) { @@ -327,19 +345,33 @@ static void* ExecThread(void* param) { } #else static void* ExecThread(void* param) { - ((Thread_StartFunc)param)(); + ((Thread_StartFunc)param)(); return NULL; } #endif -void* Thread_Create(Thread_StartFunc func) { - return Mem_Alloc(1, sizeof(pthread_t), "thread"); -} +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + pthread_t* ptr = (pthread_t*)Mem_Alloc(1, sizeof(pthread_t), "thread"); + int res; + *handle = ptr; -void Thread_Start2(void* handle, Thread_StartFunc func) { - pthread_t* ptr = (pthread_t*)handle; - int res = pthread_create(ptr, NULL, ExecThread, (void*)func); + pthread_attr_t attrs; + pthread_attr_init(&attrs); + pthread_attr_setstacksize(&attrs, stackSize); + + res = pthread_create(ptr, &attrs, ExecThread, (void*)func); if (res) Logger_Abort2(res, "Creating thread"); + pthread_attr_destroy(&attrs); + +#if defined CC_BUILD_LINUX || defined CC_BUILD_HAIKU + extern int pthread_setname_np(pthread_t thread, const char* name); + pthread_setname_np(*ptr, name); +#elif defined CC_BUILD_FREEBSD || defined CC_BUILD_OPENBSD + extern int pthread_set_name_np(pthread_t thread, const char* name); + pthread_set_name_np(*ptr, name); +#elif defined CC_BUILD_NETBSD + pthread_setname_np(*ptr, "%s", name); +#endif } void Thread_Detach(void* handle) { @@ -465,10 +497,10 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { *--------------------------------------------------------Font/Text--------------------------------------------------------* *#########################################################################################################################*/ static void FontDirCallback(const cc_string* path, void* obj) { - SysFonts_Register(path); + SysFonts_Register(path, NULL); } -void Platform_LoadSysFonts(void) { +void Platform_LoadSysFonts(void) { int i; #if defined CC_BUILD_ANDROID static const cc_string dirs[] = { @@ -505,6 +537,11 @@ void Platform_LoadSysFonts(void) { static const cc_string dirs[] = { String_FromConst("/res/fonts") }; +#elif defined CC_BUILD_OS2 + static const cc_string dirs[] = { + String_FromConst("/@unixroot/usr/share/fonts"), + String_FromConst("/@unixroot/usr/local/share/fonts") + }; #else static const cc_string dirs[] = { String_FromConst("/usr/share/fonts"), @@ -520,6 +557,10 @@ void Platform_LoadSysFonts(void) { /*########################################################################################################################* *---------------------------------------------------------Socket----------------------------------------------------------* *#########################################################################################################################*/ +#if defined CC_BUILD_OS2 +#undef AF_INET6 +#endif + union SocketAddress { struct sockaddr raw; struct sockaddr_in v4; @@ -550,14 +591,14 @@ static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int* if (res) return res; /* Prefer IPv4 addresses first */ - for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) { if (cur->ai_family != AF_INET) continue; Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); addrs[i].size = cur->ai_addrlen; i++; } - for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) { if (cur->ai_family == AF_INET) continue; Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); @@ -752,6 +793,81 @@ cc_result Process_StartOpen(const cc_string* args) { } #elif defined CC_BUILD_HAIKU || defined CC_BUILD_BEOS /* Implemented in interop_BeOS.cpp */ +#elif defined CC_BUILD_OS2 +inline static void ShowErrorMessage(const char *url) { + static char errorMsg[] = "Could not open browser. Please go to: "; + cc_string message = String_Init(errorMsg, strlen(errorMsg), 500); + String_AppendConst(&message, url); + Logger_DialogWarn(&message); +} + +cc_result Process_StartOpen(const cc_string* args) { + char str[NATIVE_STR_LEN]; + APIRET rc; + UCHAR path[CCHMAXPATH], params[100], parambuffer[500], *paramptr; + UCHAR userPath[CCHMAXPATH], sysPath[CCHMAXPATH]; + PRFPROFILE profile = { sizeof(userPath), userPath, sizeof(sysPath), sysPath }; + HINI os2Ini; + HAB hAnchor = WinQueryAnchorBlock(WinQueryActiveWindow(HWND_DESKTOP)); + RESULTCODES result = { 0 }; + PROGDETAILS details; + + // We get URL + String_EncodeUtf8(str, args); + + // Initialize buffers + Mem_Set(path, 0, sizeof(path)); + Mem_Set(parambuffer, 0, sizeof(parambuffer)); + Mem_Set(params, 0, sizeof(params)); + + // We have to look in the OS/2 configuration for the default browser. + // First step: Find the configuration files + if (!PrfQueryProfile(hAnchor, &profile)) { + ShowErrorMessage(str); + return 0; + } + + // Second step: Open the configuration files and read exe path and parameters + os2Ini = PrfOpenProfile(hAnchor, userPath); + if (os2Ini == NULLHANDLE) { + ShowErrorMessage(str); + return 0; + } + if (!PrfQueryProfileString(os2Ini, "WPURLDEFAULTSETTINGS", "DefaultBrowserExe", + NULL, path, sizeof(path))) { + PrfCloseProfile(os2Ini); + ShowErrorMessage(str); + return 0; + } + + PrfQueryProfileString(os2Ini, "WPURLDEFAULTSETTINGS", "DefaultBrowserParameters", + NULL, params, sizeof(params)); + PrfCloseProfile(os2Ini); + + // concat arguments + if (strlen(params) > 0) strncat(params, " ", 20); + strncat(params, str, sizeof(str)); + + // Build parameter buffer + strcpy(parambuffer, "Browser"); + paramptr = ¶mbuffer[strlen(parambuffer)+1]; + // copy params to buffer + strcpy(paramptr, params); + printf("params %p %p %s\n", parambuffer, paramptr, paramptr); + paramptr += strlen(params) + 1; + // To be sure: Terminate parameter list with NULL + *paramptr = '\0'; + + // Last step: Execute detached browser + rc = DosExecPgm(userPath, sizeof(userPath), EXEC_ASYNC, + parambuffer, NULL, &result, path); + if (rc != NO_ERROR) { + ShowErrorMessage(str); + return 0; + } + + return 0; +} #else cc_result Process_StartOpen(const cc_string* args) { char str[NATIVE_STR_LEN]; @@ -852,6 +968,16 @@ static cc_result Process_RawGetExePath(char* path, int* len) { *len = file.length; return 0; } +#elif defined CC_BUILD_OS2 +static cc_result Process_RawGetExePath(char* path, int* len) { + PPIB pib; + DosGetInfoBlocks(NULL, &pib); + if (pib && pib->pib_pchcmd) { + Mem_Copy(path, pib->pib_pchcmd, strlen(pib->pib_pchcmd)); + *len = strlen(pib->pib_pchcmd); + } + return 0; +} #endif @@ -933,6 +1059,12 @@ cc_bool Updater_Clean(void) { return true; } #else const struct UpdaterInfo Updater_Info = { "&eCompile latest source code to update", 0 }; #endif +#elif defined CC_BUILD_HAIKU + #if __x86_64__ + const struct UpdaterInfo Updater_Info = { "", 1, { { "OpenGL", "cc-haiku-64" } } }; + #else + const struct UpdaterInfo Updater_Info = { "&eCompile latest source code to update", 0 }; + #endif #else const struct UpdaterInfo Updater_Info = { "&eCompile latest source code to update", 0 }; #endif @@ -999,7 +1131,7 @@ const cc_string DynamicLib_Ext = String_FromConst(".dylib"); void* DynamicLib_Load2(const cc_string* path) { char str[NATIVE_STR_LEN]; String_EncodeUtf8(str, path); - return NSAddImage(str, NSADDIMAGE_OPTION_WITH_SEARCHING | + return NSAddImage(str, NSADDIMAGE_OPTION_WITH_SEARCHING | NSADDIMAGE_OPTION_RETURN_ON_ERROR); } @@ -1045,7 +1177,8 @@ void* DynamicLib_Load2(const cc_string* path) { } void* DynamicLib_Get2(void* lib, const char* name) { - return dlsym(lib, name); + void *result = dlsym(lib, name); + return result; } cc_bool DynamicLib_DescribeError(cc_string* dst) { @@ -1125,7 +1258,7 @@ void Platform_Init(void) { Platform_SingleProcess = true; #endif - Platform_InitPosix(); + Platform_InitPosix(); } #endif @@ -1161,7 +1294,7 @@ static void DecipherBlock(cc_uint32* v, const cc_uint32* key) { } #define ENC1 0xCC005EC0 -#define ENC2 0x0DA4A0DE +#define ENC2 0x0DA4A0DE #define ENC3 0xC0DED000 #define MACHINEID_LEN 32 #define ENC_SIZE 8 /* 2 32 bit ints per block */ @@ -1212,7 +1345,7 @@ static cc_result GetMachineID(cc_uint32* key) { #ifdef kIOPlatformUUIDKey registry = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); if (!registry) return ERR_NOT_SUPPORTED; - + devID = IORegistryEntryCreateCFProperty(registry, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); if (devID && CFStringGetCString(devID, tmp, sizeof(tmp), kCFStringEncodingUTF8)) { DecodeMachineID(tmp, String_Length(tmp), key); @@ -1220,13 +1353,13 @@ static cc_result GetMachineID(cc_uint32* key) { #else registry = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); - + devID = IORegistryEntryCreateCFProperty(registry, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0); if (devID && CFStringGetCString(devID, tmp, sizeof(tmp), kCFStringEncodingUTF8)) { Mem_Copy(key, tmp, MACHINEID_LEN / 2); } #endif - + if (devID) CFRelease(devID); IOObjectRelease(registry); return tmp[0] ? 0 : ERR_NOT_SUPPORTED; @@ -1289,10 +1422,10 @@ extern void GetDeviceUUID(cc_string* str); static cc_result GetMachineID(cc_uint32* key) { cc_string str; char strBuffer[STRING_SIZE]; String_InitArray(str, strBuffer); - + GetDeviceUUID(&str); if (!str.length) return ERR_NOT_SUPPORTED; - + DecodeMachineID(strBuffer, str.length, key); return 0; } diff --git a/src/Platform_Saturn.c b/src/Platform_Saturn.c new file mode 100644 index 000000000..a39079407 --- /dev/null +++ b/src/Platform_Saturn.c @@ -0,0 +1,236 @@ +#include "Core.h" +#if defined PLAT_SATURN + +#include "_PlatformBase.h" +#include "Stream.h" +#include "ExtMath.h" +#include "Funcs.h" +#include "Window.h" +#include "Utils.h" +#include "Errors.h" +#include "Options.h" +#include "PackedCol.h" +#include +#include +#include +#include + +void* calloc(size_t num, size_t size) { + void* ptr = malloc(num * size); + if (ptr) memset(ptr, 0, num * size); + return ptr; +} +#include "_PlatformConsole.h" + +const cc_result ReturnCode_FileShareViolation = 1000000000; // not used +const cc_result ReturnCode_FileNotFound = 99999; +const cc_result ReturnCode_DirectoryExists = 99999; + +const cc_result ReturnCode_SocketInProgess = -1; +const cc_result ReturnCode_SocketWouldBlock = -1; +const char* Platform_AppNameSuffix = " Saturn"; + + +/*########################################################################################################################* +*------------------------------------------------------Logging/Time-------------------------------------------------------* +*#########################################################################################################################*/ +#define WRITE_ADDRESS CS0(0x00100001) +// in Medafen, patch DummyWrite in src/ss/Cart.cpp to instead log the value + +void Platform_Log(const char* msg, int len) { + for (int i = 0; i < len; i++) { + MEMORY_WRITE(8, WRITE_ADDRESS, msg[i]); + } + MEMORY_WRITE(8, WRITE_ADDRESS, '\n'); +} + +TimeMS DateTime_CurrentUTC(void) { + return 0; +} + +void DateTime_CurrentLocal(struct DateTime* t) { + Mem_Set(t, 0, sizeof(struct DateTime)); +} + + +/*########################################################################################################################* +*--------------------------------------------------------Stopwatch--------------------------------------------------------* +*#########################################################################################################################*/ +static volatile cc_uint32 overflow_count; + +cc_uint64 Stopwatch_Measure(void) { + return cpu_frt_count_get() + (overflow_count * 65536); +} + +cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { + if (end < beg) return 0; + return (end - beg); // TODO measure time +} + +static void ovf_handler(void) { overflow_count++; } + +static void Stopwatch_Init(void) { + cpu_frt_init(CPU_FRT_CLOCK_DIV_8); + cpu_frt_ovi_set(ovf_handler); + + cpu_frt_interrupt_priority_set(15); + cpu_frt_count_set(0); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Directory/File------------------------------------------------------* +*#########################################################################################################################*/ +cc_result Directory_Create(const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +int File_Exists(const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Open(cc_file* file, const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Create(cc_file* file, const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* bytesWrote) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Close(cc_file file) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Seek(cc_file file, int offset, int seekType) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Position(cc_file file, cc_uint32* pos) { + return ERR_NOT_SUPPORTED; +} + +cc_result File_Length(cc_file file, cc_uint32* len) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Threading--------------------------------------------------------* +*#########################################################################################################################*/ +void Thread_Sleep(cc_uint32 milliseconds) { + // TODO sleep a bit +} + +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + *handle = NULL; +} + +void Thread_Detach(void* handle) { +} + +void Thread_Join(void* handle) { +} + +void* Mutex_Create(void) { + return NULL; +} + +void Mutex_Free(void* handle) { +} + +void Mutex_Lock(void* handle) { +} + +void Mutex_Unlock(void* handle) { +} + +void* Waitable_Create(void) { + return NULL; +} + +void Waitable_Free(void* handle) { +} + +void Waitable_Signal(void* handle) { +} + +void Waitable_Wait(void* handle) { +} + +void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { +} + + +/*########################################################################################################################* +*---------------------------------------------------------Socket----------------------------------------------------------* +*#########################################################################################################################*/ +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + return ERR_NOT_SUPPORTED; +} + +void Socket_Close(cc_socket s) { +} + +cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable) { + return ERR_NOT_SUPPORTED; +} + +cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Platform---------------------------------------------------------* +*#########################################################################################################################*/ +void Platform_Init(void) { + Stopwatch_Init(); +} + +void Platform_Free(void) { } + +cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*-------------------------------------------------------Encryption--------------------------------------------------------* +*#########################################################################################################################*/ +static cc_result GetMachineID(cc_uint32* key) { + return ERR_NOT_SUPPORTED; +} +#endif diff --git a/src/Platform_Switch.c b/src/Platform_Switch.c new file mode 100644 index 000000000..271e31f1c --- /dev/null +++ b/src/Platform_Switch.c @@ -0,0 +1,540 @@ +#include "Core.h" +#if defined CC_BUILD_SWITCH +#include "_PlatformBase.h" +#include "Stream.h" +#include "ExtMath.h" +#include "Funcs.h" +#include "Window.h" +#include "Utils.h" +#include "Errors.h" +#include "Options.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "_PlatformConsole.h" + +const cc_result ReturnCode_FileShareViolation = 1000000000; // not used +const cc_result ReturnCode_FileNotFound = ENOENT; +const cc_result ReturnCode_SocketInProgess = EINPROGRESS; +const cc_result ReturnCode_SocketWouldBlock = EWOULDBLOCK; +const cc_result ReturnCode_DirectoryExists = EEXIST; +const char* Platform_AppNameSuffix = " Switch"; + +alignas(16) u8 __nx_exception_stack[0x1000]; +u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + +void __libnx_exception_handler(ThreadExceptionDump *ctx) +{ + int i; + FILE *f = fopen("sdmc:/exception_dump", "w"); + if(f==NULL)return; + + fprintf(f, "error_desc: 0x%x\n", ctx->error_desc);//You can also parse this with ThreadExceptionDesc. + //This assumes AArch64, however you can also use threadExceptionIsAArch64(). + for(i=0; i<29; i++)fprintf(f, "[X%d]: 0x%lx\n", i, ctx->cpu_gprs[i].x); + fprintf(f, "fp: 0x%lx\n", ctx->fp.x); + fprintf(f, "lr: 0x%lx\n", ctx->lr.x); + fprintf(f, "sp: 0x%lx\n", ctx->sp.x); + fprintf(f, "pc: 0x%lx\n", ctx->pc.x); + + //You could print fpu_gprs if you want. + + fprintf(f, "pstate: 0x%x\n", ctx->pstate); + fprintf(f, "afsr0: 0x%x\n", ctx->afsr0); + fprintf(f, "afsr1: 0x%x\n", ctx->afsr1); + fprintf(f, "esr: 0x%x\n", ctx->esr); + + fprintf(f, "far: 0x%lx\n", ctx->far.x); + + fclose(f); +} + + +/*########################################################################################################################* +*------------------------------------------------------Logging/Time-------------------------------------------------------* +*#########################################################################################################################*/ +cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { + if (end < beg) return 0; + + // See include/switch/arm/counter.h + // static inline u64 armTicksToNs(u64 tick) { return (tick * 625) / 12; } + return ((end - beg) * 625) / 12000; +} + +cc_uint64 Stopwatch_Measure(void) { + return armGetSystemTick(); +} + +void Platform_Log(const char* msg, int len) { + svcOutputDebugString(msg, len); +} + +TimeMS DateTime_CurrentUTC(void) { + u64 timestamp = 0; + timeGetCurrentTime(TimeType_Default, ×tamp); + return timestamp + UNIX_EPOCH_SECONDS; +} + +void DateTime_CurrentLocal(struct DateTime* t) { + u64 timestamp = 0; + TimeCalendarTime calTime = { 0 }; + timeGetCurrentTime(TimeType_Default, ×tamp); + timeToCalendarTimeWithMyRule(timestamp, &calTime, NULL); + + t->year = calTime.year; + t->month = calTime.month; + t->day = calTime.day; + t->hour = calTime.hour; + t->minute = calTime.minute; + t->second = calTime.second; +} + + +/*########################################################################################################################* +*-----------------------------------------------------Directory/File------------------------------------------------------* +*#########################################################################################################################*/ +static const cc_string root_path = String_FromConst("sdmc:/switch/ClassiCube/"); + +static void GetNativePath(char* str, const cc_string* path) { + Mem_Copy(str, root_path.buffer, root_path.length); + str += root_path.length; + String_EncodeUtf8(str, path); +} + +cc_result Directory_Create(const cc_string* path) { + char str[NATIVE_STR_LEN]; + GetNativePath(str, path); + return mkdir(str, 0) == -1 ? errno : 0; +} + +int File_Exists(const cc_string* path) { + char str[NATIVE_STR_LEN]; + struct stat sb; + GetNativePath(str, path); + return stat(str, &sb) == 0 && S_ISREG(sb.st_mode); +} + +cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { + cc_string path; char pathBuffer[FILENAME_SIZE]; + char str[NATIVE_STR_LEN]; + struct dirent* entry; + int res; + + GetNativePath(str, dirPath); + DIR* dirPtr = opendir(str); + if (!dirPtr) return errno; + + // POSIX docs: "When the end of the directory is encountered, a null pointer is returned and errno is not changed." + // errno is sometimes leftover from previous calls, so always reset it before readdir gets called + errno = 0; + String_InitArray(path, pathBuffer); + + while ((entry = readdir(dirPtr))) { + path.length = 0; + String_Format1(&path, "%s/", dirPath); + + // ignore . and .. entry + char* src = entry->d_name; + if (src[0] == '.' && src[1] == '\0') continue; + if (src[0] == '.' && src[1] == '.' && src[2] == '\0') continue; + + int len = String_Length(src); + String_AppendUtf8(&path, src, len); + int is_dir = entry->d_type == DT_DIR; + // TODO: fallback to stat when this fails + + if (is_dir) { + res = Directory_Enum(&path, obj, callback); + if (res) { closedir(dirPtr); return res; } + } else { + callback(&path, obj); + } + errno = 0; + } + + res = errno; // return code from readdir + closedir(dirPtr); + return res; +} + +static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { + char str[NATIVE_STR_LEN]; + GetNativePath(str, path); + *file = open(str, mode, 0); + return *file == -1 ? errno : 0; +} + +cc_result File_Open(cc_file* file, const cc_string* path) { + return File_Do(file, path, O_RDONLY); +} +cc_result File_Create(cc_file* file, const cc_string* path) { + return File_Do(file, path, O_RDWR | O_CREAT | O_TRUNC); +} +cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { + return File_Do(file, path, O_RDWR | O_CREAT); +} + +cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) { + *bytesRead = read(file, data, count); + return *bytesRead == -1 ? errno : 0; +} + +cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* bytesWrote) { + *bytesWrote = write(file, data, count); + return *bytesWrote == -1 ? errno : 0; +} + +cc_result File_Close(cc_file file) { + return close(file) == -1 ? errno : 0; +} + +cc_result File_Seek(cc_file file, int offset, int seekType) { + static cc_uint8 modes[3] = { SEEK_SET, SEEK_CUR, SEEK_END }; + return lseek(file, offset, modes[seekType]) == -1 ? errno : 0; +} + +cc_result File_Position(cc_file file, cc_uint32* pos) { + *pos = lseek(file, 0, SEEK_CUR); + return *pos == -1 ? errno : 0; +} + +cc_result File_Length(cc_file file, cc_uint32* len) { + struct stat st; + if (fstat(file, &st) == -1) { *len = -1; return errno; } + *len = st.st_size; return 0; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Threading--------------------------------------------------------* +*#########################################################################################################################*/ +void Thread_Sleep(cc_uint32 milliseconds) { + cc_uint64 timeout_ns = (cc_uint64)milliseconds * (1000 * 1000); // to nanoseconds + svcSleepThread(timeout_ns); +} + +static void ExecSwitchThread(void* param) { + ((Thread_StartFunc)param)(); +} + +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + Thread* thread = (Thread*)Mem_Alloc(1, sizeof(Thread), name); + *handle = thread; + + threadCreate(thread, ExecSwitchThread, (void*)func, NULL, stackSize, 0x2C, -2); + threadStart(thread); +} + +void Thread_Detach(void* handle) { + // threadClose frees up resources, **including the stack of the thread** + // Which obviously completely breaks the thread - so instead just accept + // that there will be a small memory leak when non-joined threads exit +} + +void Thread_Join(void* handle) { + Thread* thread = (Thread*)handle; + threadWaitForExit(thread); + threadClose(thread); + Mem_Free(thread); +} + +void* Mutex_Create(void) { + Mutex* mutex = (Mutex*)Mem_Alloc(1, sizeof(Mutex), "mutex"); + mutexInit(mutex); + return mutex; +} + +void Mutex_Free(void* handle) { + Mem_Free(handle); +} + +void Mutex_Lock(void* handle) { + mutexLock((Mutex*)handle); +} + +void Mutex_Unlock(void* handle) { + mutexUnlock((Mutex*)handle); +} + + +struct WaitData { + CondVar cond; + Mutex mutex; + int signalled; // For when Waitable_Signal is called before Waitable_Wait +}; + +void* Waitable_Create(void) { + struct WaitData* ptr = (struct WaitData*)Mem_Alloc(1, sizeof(struct WaitData), "waitable"); + + mutexInit(&ptr->mutex); + condvarInit(&ptr->cond); + + ptr->signalled = false; + return ptr; +} + +void Waitable_Free(void* handle) { + struct WaitData* ptr = (struct WaitData*)handle; + Mem_Free(ptr); +} + +void Waitable_Signal(void* handle) { + struct WaitData* ptr = (struct WaitData*)handle; + + Mutex_Lock(&ptr->mutex); + condvarWakeOne(&ptr->cond); + Mutex_Unlock(&ptr->mutex); + + ptr->signalled = true; +} + +void Waitable_Wait(void* handle) { + struct WaitData* ptr = (struct WaitData*)handle; + + Mutex_Lock(&ptr->mutex); + if (!ptr->signalled) { + condvarWait(&ptr->cond, &ptr->mutex); + } + ptr->signalled = false; + Mutex_Unlock(&ptr->mutex); +} + +void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { + struct WaitData* ptr = (struct WaitData*)handle; + cc_uint64 timeout_ns = (cc_uint64)milliseconds * (1000 * 1000); // to nanoseconds + + Mutex_Lock(&ptr->mutex); + if (!ptr->signalled) { + condvarWaitTimeout(&ptr->cond, &ptr->mutex, timeout_ns); + } + ptr->signalled = false; + Mutex_Unlock(&ptr->mutex); +} +/* + +void* Waitable_Create(void) { + LEvent* ptr = (LEvent*)Mem_Alloc(1, sizeof(LEvent), "waitable"); + leventInit(ptr, false, true); + return ptr; +} + +void Waitable_Free(void* handle) { + LEvent* ptr = (LEvent*)handle; + leventClear(ptr); + Mem_Free(ptr); +} + +void Waitable_Signal(void* handle) { + //leventSignal((LEvent*)handle); +} + +void Waitable_Wait(void* handle) { + leventWait((LEvent*)handle, UINT64_MAX); +} + +void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { + cc_uint64 timeout_ns = milliseconds * (1000 * 1000); // to nanoseconds + leventWait((LEvent*)handle, timeout_ns); +} +*/ + +/*########################################################################################################################* +*---------------------------------------------------------Socket----------------------------------------------------------* +*#########################################################################################################################*/ +union SocketAddress { + struct sockaddr raw; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + struct sockaddr_storage total; +}; + +static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { + char portRaw[32]; cc_string portStr; + struct addrinfo hints = { 0 }; + struct addrinfo* result; + struct addrinfo* cur; + int res, i = 0; + + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + String_InitArray(portStr, portRaw); + String_AppendInt(&portStr, port); + portRaw[portStr.length] = '\0'; + + res = getaddrinfo(host, portRaw, &hints, &result); + if (res == EAI_AGAIN) return SOCK_ERR_UNKNOWN_HOST; + if (res) return res; + + /* Prefer IPv4 addresses first */ + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + { + if (cur->ai_family != AF_INET) continue; + Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); + addrs[i].size = cur->ai_addrlen; i++; + } + + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + { + if (cur->ai_family == AF_INET) continue; + Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); + addrs[i].size = cur->ai_addrlen; i++; + } + + freeaddrinfo(result); + *numValidAddrs = i; + return i == 0 ? ERR_INVALID_ARGUMENT : 0; +} + +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) { + union SocketAddress* addr = (union SocketAddress*)addrs[0].data; + char str[NATIVE_STR_LEN]; + + String_EncodeUtf8(str, address); + *numValidAddrs = 0; + + if (inet_pton(AF_INET, str, &addr->v4.sin_addr) > 0) { + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = htons(port); + + addrs[0].size = sizeof(addr->v4); + *numValidAddrs = 1; + return 0; + } + + if (inet_pton(AF_INET6, str, &addr->v6.sin6_addr) > 0) { + addr->v6.sin6_family = AF_INET6; + addr->v6.sin6_port = htons(port); + + addrs[0].size = sizeof(addr->v6); + *numValidAddrs = 1; + return 0; + } + + return ParseHost(str, port, addrs, numValidAddrs); +} + +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { + struct sockaddr* raw = (struct sockaddr*)addr->data; + cc_result res; + + *s = socket(raw->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (*s == -1) return errno; + + if (nonblocking) { + fcntl(*s, F_SETFL, O_NONBLOCK); + } + + res = connect(*s, raw, addr->size); + return res == -1 ? errno : 0; +} + +cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + int recvCount = recv(s, data, count, 0); + if (recvCount != -1) { *modified = recvCount; return 0; } + *modified = 0; return errno; +} + +cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + int sentCount = send(s, data, count, 0); + if (sentCount != -1) { *modified = sentCount; return 0; } + *modified = 0; return errno; +} + +void Socket_Close(cc_socket s) { + shutdown(s, SHUT_RDWR); + close(s); +} + +static cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) { + struct pollfd pfd; + int flags; + + pfd.fd = s; + pfd.events = mode == SOCKET_POLL_READ ? POLLIN : POLLOUT; + if (poll(&pfd, 1, 0) == -1) { *success = false; return errno; } + + /* to match select, closed socket still counts as readable */ + flags = mode == SOCKET_POLL_READ ? (POLLIN | POLLHUP) : POLLOUT; + *success = (pfd.revents & flags) != 0; + return 0; +} + +cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable) { + return Socket_Poll(s, SOCKET_POLL_READ, readable); +} + +cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { + socklen_t resultSize = sizeof(socklen_t); + cc_result res = Socket_Poll(s, SOCKET_POLL_WRITE, writable); + if (res || *writable) return res; + + /* https://stackoverflow.com/questions/29479953/so-error-value-after-successful-socket-operation */ + getsockopt(s, SOL_SOCKET, SO_ERROR, &res, &resultSize); + return res; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Platform---------------------------------------------------------* +*#########################################################################################################################*/ +static void CreateRootDirectory(void) { + mkdir("sdmc:/switch", 0); + int res = mkdir(root_path.buffer, 0); + int err = res == -1 ? errno : 0; + Platform_Log1("Created root directory: %i", &err); +} + +void Platform_Init(void) { + CreateRootDirectory(); + socketInitializeDefault(); +} + +void Platform_Free(void) { + socketExit(); +} + +cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { + char chars[NATIVE_STR_LEN]; + int len; + + /* For unrecognised error codes, strerror_r might return messages */ + /* such as 'No error information', which is not very useful */ + /* (could check errno here but quicker just to skip entirely) */ + if (res >= 1000) return false; + + len = strerror_r(res, chars, NATIVE_STR_LEN); + if (len == -1) return false; + + len = String_CalcLen(chars, NATIVE_STR_LEN); + String_AppendUtf8(dst, chars, len); + return true; +} + +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*-------------------------------------------------------Encryption--------------------------------------------------------* +*#########################################################################################################################*/ +static cc_result GetMachineID(cc_uint32* key) { + return ERR_NOT_SUPPORTED; +} +#endif diff --git a/src/Platform_Web.c b/src/Platform_Web.c index f1b43bec2..2fd862701 100644 --- a/src/Platform_Web.c +++ b/src/Platform_Web.c @@ -84,11 +84,10 @@ void Platform_Log(const char* msg, int len) { interop_Log(msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } extern void interop_GetLocalTime(struct DateTime* t); diff --git a/src/Platform_WiiU.c b/src/Platform_WiiU.c new file mode 100644 index 000000000..a102a9775 --- /dev/null +++ b/src/Platform_WiiU.c @@ -0,0 +1,465 @@ +#include "Core.h" +#if defined CC_BUILD_WIIU + +#include "_PlatformBase.h" +#include "Stream.h" +#include "ExtMath.h" +#include "SystemFonts.h" +#include "Funcs.h" +#include "Window.h" +#include "Utils.h" +#include "Errors.h" +#include "PackedCol.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "_PlatformConsole.h" + +const cc_result ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */ +const cc_result ReturnCode_FileNotFound = ENOENT; +const cc_result ReturnCode_SocketInProgess = EINPROGRESS; +const cc_result ReturnCode_SocketWouldBlock = EWOULDBLOCK; +const cc_result ReturnCode_DirectoryExists = EEXIST; + +const char* Platform_AppNameSuffix = " Wii U"; + + +/*########################################################################################################################* +*------------------------------------------------------Logging/Time-------------------------------------------------------* +*#########################################################################################################################*/ +void Platform_Log(const char* msg, int len) { + char tmp[256 + 1]; + len = min(len, 256); + Mem_Copy(tmp, msg, len); tmp[len] = '\0'; + + OSReport("%s\n", tmp); +} +#define WIIU_EPOCH_ADJUST 946684800ULL // Wii U time epoch is year 2000, not 1970 + +TimeMS DateTime_CurrentUTC(void) { + OSTime time = OSGetTime(); + cc_int64 secs = (time_t)OSTicksToSeconds(time); + return secs + UNIX_EPOCH_SECONDS + WIIU_EPOCH_ADJUST; +} + +void DateTime_CurrentLocal(struct DateTime* t) { + struct OSCalendarTime loc_time; + OSTicksToCalendarTime(OSGetTime(), &loc_time); + + t->year = loc_time.tm_year; + t->month = loc_time.tm_mon + 1; + t->day = loc_time.tm_mday; + t->hour = loc_time.tm_hour; + t->minute = loc_time.tm_min; + t->second = loc_time.tm_sec; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Stopwatch--------------------------------------------------------* +*#########################################################################################################################*/ +cc_uint64 Stopwatch_Measure(void) { + return OSGetSystemTime(); // TODO OSGetSystemTick ?? +} + +cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { + if (end < beg) return 0; + return OSTicksToMicroseconds(end - beg); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Directory/File------------------------------------------------------* +*#########################################################################################################################*/ +// fs:/vol/external01 +//const char* sd_root = WHBGetSdCardMountPath(); + //int sd_length = String_Length(sd_root); + //Mem_Copy(str, sd_root, sd_length); + //str += sd_length; + +static const cc_string root_path = String_FromConst("ClassiCube/"); + +static void GetNativePath(char* str, const cc_string* path) { + Mem_Copy(str, root_path.buffer, root_path.length); + str += root_path.length; + String_EncodeUtf8(str, path); +} + +cc_result Directory_Create(const cc_string* path) { + char str[NATIVE_STR_LEN]; + GetNativePath(str, path); + /* read/write/search permissions for owner and group, and with read/search permissions for others. */ + /* TODO: Is the default mode in all cases */ + return mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1 ? errno : 0; +} + +int File_Exists(const cc_string* path) { + char str[NATIVE_STR_LEN]; + struct stat sb; + GetNativePath(str, path); + return stat(str, &sb) == 0 && S_ISREG(sb.st_mode); +} + +cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { + cc_string path; char pathBuffer[FILENAME_SIZE]; + char str[NATIVE_STR_LEN]; + DIR* dirPtr; + struct dirent* entry; + char* src; + int len, res, is_dir; + + GetNativePath(str, dirPath); + dirPtr = opendir(str); + if (!dirPtr) return errno; + + /* POSIX docs: "When the end of the directory is encountered, a null pointer is returned and errno is not changed." */ + /* errno is sometimes leftover from previous calls, so always reset it before readdir gets called */ + errno = 0; + String_InitArray(path, pathBuffer); + + while ((entry = readdir(dirPtr))) { + path.length = 0; + String_Format1(&path, "%s/", dirPath); + + /* ignore . and .. entry */ + src = entry->d_name; + if (src[0] == '.' && src[1] == '\0') continue; + if (src[0] == '.' && src[1] == '.' && src[2] == '\0') continue; + + len = String_Length(src); + String_AppendUtf8(&path, src, len); + + is_dir = entry->d_type == DT_DIR; + /* TODO: fallback to stat when this fails */ + + if (is_dir) { + res = Directory_Enum(&path, obj, callback); + if (res) { closedir(dirPtr); return res; } + } else { + callback(&path, obj); + } + errno = 0; + } + + res = errno; /* return code from readdir */ + closedir(dirPtr); + return res; +} + +static cc_result File_Do(cc_file* file, const cc_string* path, int mode) { + char str[NATIVE_STR_LEN]; + GetNativePath(str, path); + *file = open(str, mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + return *file == -1 ? errno : 0; +} + +cc_result File_Open(cc_file* file, const cc_string* path) { + return File_Do(file, path, O_RDONLY); +} +cc_result File_Create(cc_file* file, const cc_string* path) { + return File_Do(file, path, O_RDWR | O_CREAT | O_TRUNC); +} +cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { + return File_Do(file, path, O_RDWR | O_CREAT); +} + +cc_result File_Read(cc_file file, void* data, cc_uint32 count, cc_uint32* bytesRead) { + *bytesRead = read(file, data, count); + return *bytesRead == -1 ? errno : 0; +} + +cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* bytesWrote) { + *bytesWrote = write(file, data, count); + return *bytesWrote == -1 ? errno : 0; +} + +cc_result File_Close(cc_file file) { + return close(file) == -1 ? errno : 0; +} + +cc_result File_Seek(cc_file file, int offset, int seekType) { + static cc_uint8 modes[3] = { SEEK_SET, SEEK_CUR, SEEK_END }; + return lseek(file, offset, modes[seekType]) == -1 ? errno : 0; +} + +cc_result File_Position(cc_file file, cc_uint32* pos) { + *pos = lseek(file, 0, SEEK_CUR); + return *pos == -1 ? errno : 0; +} + +cc_result File_Length(cc_file file, cc_uint32* len) { + struct stat st; + if (fstat(file, &st) == -1) { *len = -1; return errno; } + *len = st.st_size; return 0; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Threading--------------------------------------------------------* +*#########################################################################################################################*/ +void Thread_Sleep(cc_uint32 milliseconds) { + OSSleepTicks(OSMillisecondsToTicks(milliseconds)); +} + +static int ExecThread(int argc, const char **argv) { + ((Thread_StartFunc)argv)(); + return 0; +} + +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { + OSThread* thread = (OSThread*)Mem_Alloc(1, sizeof(OSThread), "thread"); + void* stack = memalign(16, stackSize); + + OSCreateThread(thread, ExecThread, + 1, (Thread_StartFunc)func, + stack + stackSize, stackSize, + 16, OS_THREAD_ATTRIB_AFFINITY_ANY); + + *handle = thread; + // TODO revisit this + OSSetThreadRunQuantum(thread, 1000); // force yield after 1 millisecond + OSResumeThread(thread); +} + +void Thread_Detach(void* handle) { + OSDetachThread((OSThread*)handle); +} + +void Thread_Join(void* handle) { + int result; + OSJoinThread((OSThread*)handle, &result); +} + +void* Mutex_Create(void) { + OSFastMutex* mutex = (OSFastMutex*)Mem_Alloc(1, sizeof(OSFastMutex), "mutex"); + + OSFastMutex_Init(mutex, "CC mutex"); + return mutex; +} + +void Mutex_Free(void* handle) { + Mem_Free(handle); +} + +void Mutex_Lock(void* handle) { + OSFastMutex_Lock((OSFastMutex*)handle); +} + +void Mutex_Unlock(void* handle) { + OSFastMutex_Unlock((OSFastMutex*)handle); +} + +void* Waitable_Create(void) { + OSEvent* event = (OSEvent*)Mem_Alloc(1, sizeof(OSEvent), "waitable"); + + OSInitEvent(event, false, OS_EVENT_MODE_AUTO); + return event; +} + +void Waitable_Free(void* handle) { + OSResetEvent((OSEvent*)handle); + Mem_Free(handle); +} + +void Waitable_Signal(void* handle) { + OSSignalEvent((OSEvent*)handle); +} + +void Waitable_Wait(void* handle) { + OSWaitEvent((OSEvent*)handle); +} + +void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { + OSTime timeout = OSMillisecondsToTicks(milliseconds); + OSWaitEventWithTimeout((OSEvent*)handle, timeout); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Socket----------------------------------------------------------* +*#########################################################################################################################*/ +union SocketAddress { + struct sockaddr raw; + struct sockaddr_in v4; + struct sockaddr_storage total; +}; + +static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { + char portRaw[32]; cc_string portStr; + struct addrinfo hints = { 0 }; + struct addrinfo* result; + struct addrinfo* cur; + int res, i = 0; + + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + String_InitArray(portStr, portRaw); + String_AppendInt(&portStr, port); + portRaw[portStr.length] = '\0'; + + res = getaddrinfo(host, portRaw, &hints, &result); + if (res == EAI_AGAIN) return SOCK_ERR_UNKNOWN_HOST; + if (res) return res; + + /* Prefer IPv4 addresses first */ + // TODO: Wii U has no ipv6 support anyways? + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + { + if (cur->ai_family != AF_INET) continue; + Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); + addrs[i].size = cur->ai_addrlen; i++; + } + + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + { + if (cur->ai_family == AF_INET) continue; + Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); + addrs[i].size = cur->ai_addrlen; i++; + } + + freeaddrinfo(result); + *numValidAddrs = i; + return i == 0 ? ERR_INVALID_ARGUMENT : 0; +} + +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) { + union SocketAddress* addr = (union SocketAddress*)addrs[0].data; + char str[NATIVE_STR_LEN]; + + String_EncodeUtf8(str, address); + *numValidAddrs = 0; + + if (inet_pton(AF_INET, str, &addr->v4.sin_addr) > 0) { + addr->v4.sin_family = AF_INET; + addr->v4.sin_port = htons(port); + + addrs[0].size = sizeof(addr->v4); + *numValidAddrs = 1; + return 0; + } + return ParseHost(str, port, addrs, numValidAddrs); +} + +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { + struct sockaddr* raw = (struct sockaddr*)addr->data; + cc_result res; + + *s = socket(raw->sa_family, SOCK_STREAM, IPPROTO_TCP); + if (*s == -1) return errno; + + if (nonblocking) { + int blocking_raw = -1; /* non-blocking mode */ + ioctl(*s, FIONBIO, &blocking_raw); + } + + res = connect(*s, raw, addr->size); + return res == -1 ? errno : 0; +} + +cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + int recvCount = recv(s, data, count, 0); + if (recvCount != -1) { *modified = recvCount; return 0; } + *modified = 0; return errno; +} + +cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified) { + int sentCount = send(s, data, count, 0); + if (sentCount != -1) { *modified = sentCount; return 0; } + *modified = 0; return errno; +} + +void Socket_Close(cc_socket s) { + shutdown(s, SHUT_RDWR); + close(s); +} + +static cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) { + struct pollfd pfd; + int flags; + + pfd.fd = s; + pfd.events = mode == SOCKET_POLL_READ ? POLLIN : POLLOUT; + if (poll(&pfd, 1, 0) == -1) { *success = false; return errno; } + + /* to match select, closed socket still counts as readable */ + flags = mode == SOCKET_POLL_READ ? (POLLIN | POLLHUP) : POLLOUT; + *success = (pfd.revents & flags) != 0; + return 0; +} + +cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable) { + return Socket_Poll(s, SOCKET_POLL_READ, readable); +} + +cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { + socklen_t resultSize = sizeof(socklen_t); + cc_result res = Socket_Poll(s, SOCKET_POLL_WRITE, writable); + if (res || *writable) return res; + + /* https://stackoverflow.com/questions/29479953/so-error-value-after-successful-socket-operation */ + getsockopt(s, SOL_SOCKET, SO_ERROR, &res, &resultSize); + return res; +} + + +/*########################################################################################################################* +*--------------------------------------------------------Platform---------------------------------------------------------* +*#########################################################################################################################*/ +cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { + char chars[NATIVE_STR_LEN]; + int len; + + /* For unrecognised error codes, strerror_r might return messages */ + /* such as 'No error information', which is not very useful */ + /* (could check errno here but quicker just to skip entirely) */ + if (res >= 1000) return false; + + len = strerror_r(res, chars, NATIVE_STR_LEN); + if (len == -1) return false; + + len = String_CalcLen(chars, NATIVE_STR_LEN); + String_AppendUtf8(dst, chars, len); + return true; +} + +void Platform_Init(void) { + WHBProcInit(); + mkdir("ClassiCube", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +} + +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + + +/*########################################################################################################################* +*-------------------------------------------------------Encryption--------------------------------------------------------* +*#########################################################################################################################*/ +static cc_result GetMachineID(cc_uint32* key) { return ERR_NOT_SUPPORTED; } +#endif diff --git a/src/Platform_Windows.c b/src/Platform_Windows.c index 9db4f92e8..a0633559f 100644 --- a/src/Platform_Windows.c +++ b/src/Platform_Windows.c @@ -96,18 +96,18 @@ void Platform_Log(const char* msg, int len) { OutputDebugStringA("\n"); } -#define FILETIME_EPOCH 50491123200000ULL -#define FILETIME_UNIX_EPOCH 11644473600LL -#define FileTime_TotalMS(time) ((time / 10000) + FILETIME_EPOCH) -#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) -TimeMS DateTime_CurrentUTC_MS(void) { +#define FILETIME_EPOCH 50491123200ULL +#define FILETIME_UNIX_EPOCH 11644473600ULL +#define FileTime_TotalSecs(time) ((time / 10000000) + FILETIME_EPOCH) +#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) +TimeMS DateTime_CurrentUTC(void) { FILETIME ft; cc_uint64 raw; GetSystemTimeAsFileTime(&ft); /* in 100 nanosecond units, since Jan 1 1601 */ raw = ft.dwLowDateTime | ((cc_uint64)ft.dwHighDateTime << 32); - return FileTime_TotalMS(raw); + return FileTime_TotalSecs(raw); } void DateTime_CurrentLocal(struct DateTime* t) { @@ -296,17 +296,13 @@ static DWORD WINAPI ExecThread(void* param) { return 0; } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { DWORD threadID; - void* handle = CreateThread(NULL, 0, ExecThread, (void*)func, CREATE_SUSPENDED, &threadID); - if (!handle) { - Logger_Abort2(GetLastError(), "Creating thread"); - } - return handle; -} - -void Thread_Start2(void* handle, Thread_StartFunc func) { - ResumeThread((HANDLE)handle); + HANDLE thread = CreateThread(NULL, 0, ExecThread, (void*)func, CREATE_SUSPENDED, &threadID); + if (!thread) Logger_Abort2(GetLastError(), "Creating thread"); + + *handle = thread; + ResumeThread(thread); } void Thread_Detach(void* handle) { @@ -364,7 +360,7 @@ static void FontDirCallback(const cc_string* path, void* obj) { static const cc_string fonExt = String_FromConst(".fon"); /* Completely skip windows .FON files */ if (String_CaselessEnds(path, &fonExt)) return; - SysFonts_Register(path); + SysFonts_Register(path, NULL); } void Platform_LoadSysFonts(void) { @@ -843,7 +839,7 @@ cc_bool DynamicLib_DescribeError(cc_string* dst) { dynamicErr = 0; /* Reset error (match posix behaviour) */ Platform_DescribeError(res, dst); - String_Format1(dst, " (error %i)", &res); + String_Format1(dst, " (error %e)", &res); /* Plugin may have been compiled to load symbols from ClassiCube.exe, */ /* but the user might have renamed it to something else */ diff --git a/src/Platform_Xbox.c b/src/Platform_Xbox.c index 3ffd446eb..cea3e781e 100644 --- a/src/Platform_Xbox.c +++ b/src/Platform_Xbox.c @@ -39,16 +39,16 @@ void Platform_Log(const char* msg, int len) { OutputDebugStringA(tmp); } -#define FILETIME_EPOCH 50491123200000ULL -#define FILETIME_UNIX_EPOCH 11644473600LL -#define FileTime_TotalMS(time) ((time / 10000) + FILETIME_EPOCH) -#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) -TimeMS DateTime_CurrentUTC_MS(void) { +#define FILETIME_EPOCH 50491123200ULL +#define FILETIME_UNIX_EPOCH 11644473600ULL +#define FileTime_TotalSecs(time) ((time / 10000000) + FILETIME_EPOCH) +#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) +TimeMS DateTime_CurrentUTC(void) { LARGE_INTEGER ft; KeQuerySystemTime(&ft); /* in 100 nanosecond units, since Jan 1 1601 */ - return FileTime_TotalMS(ft.QuadPart); + return FileTime_TotalSecs(ft.QuadPart); } void DateTime_CurrentLocal(struct DateTime* t) { @@ -64,10 +64,10 @@ void DateTime_CurrentLocal(struct DateTime* t) { } /* TODO: check this is actually accurate */ -static cc_uint64 sw_freqMul = 1, sw_freqDiv = 1; +static cc_uint64 sw_freqDiv = 1; cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { if (end < beg) return 0; - return ((end - beg) * sw_freqMul) / sw_freqDiv; + return ((end - beg) * 1000000ULL) / sw_freqDiv; } cc_uint64 Stopwatch_Measure(void) { @@ -76,9 +76,7 @@ cc_uint64 Stopwatch_Measure(void) { static void Stopwatch_Init(void) { ULONGLONG freq = KeQueryPerformanceFrequency(); - - sw_freqMul = 1000 * 1000; - sw_freqDiv = freq; + sw_freqDiv = freq; } @@ -171,8 +169,6 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall } static cc_result DoFile(cc_file* file, const cc_string* path, DWORD access, DWORD createMode) { - if (!hdd_mounted) return ERR_NOT_SUPPORTED; - char str[NATIVE_STR_LEN]; GetNativePath(str, path); cc_result res; @@ -182,12 +178,17 @@ static cc_result DoFile(cc_file* file, const cc_string* path, DWORD access, DWOR } cc_result File_Open(cc_file* file, const cc_string* path) { + if (!hdd_mounted) return ReturnCode_FileNotFound; return DoFile(file, path, GENERIC_READ, OPEN_EXISTING); } + cc_result File_Create(cc_file* file, const cc_string* path) { + if (!hdd_mounted) return ERR_NOT_SUPPORTED; return DoFile(file, path, GENERIC_WRITE | GENERIC_READ, CREATE_ALWAYS); } + cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { + if (!hdd_mounted) return ERR_NOT_SUPPORTED; return DoFile(file, path, GENERIC_WRITE | GENERIC_READ, OPEN_ALWAYS); } @@ -232,17 +233,13 @@ static DWORD WINAPI ExecThread(void* param) { return 0; } -void* Thread_Create(Thread_StartFunc func) { +void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* name) { DWORD threadID; - void* handle = CreateThread(NULL, 0, ExecThread, (void*)func, CREATE_SUSPENDED, &threadID); - if (!handle) { - Logger_Abort2(GetLastError(), "Creating thread"); - } - return handle; -} + HANDLE thread = CreateThread(NULL, stackSize, ExecThread, (void*)func, CREATE_SUSPENDED, &threadID); + if (!thread) Logger_Abort2(GetLastError(), "Creating thread"); -void Thread_Start2(void* handle, Thread_StartFunc func) { - NtResumeThread((HANDLE)handle, NULL); + *handle = thread; + NtResumeThread(thread, NULL); } void Thread_Detach(void* handle) { @@ -421,6 +418,11 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { return false; } +cc_bool Process_OpenSupported = false; +cc_result Process_StartOpen(const cc_string* args) { + return ERR_NOT_SUPPORTED; +} + /*########################################################################################################################* *-------------------------------------------------------Encryption--------------------------------------------------------* diff --git a/src/Platform_Xbox360.c b/src/Platform_Xbox360.c index ff60a1439..0bd3e6e65 100644 --- a/src/Platform_Xbox360.c +++ b/src/Platform_Xbox360.c @@ -13,6 +13,7 @@ #include #include #include +#include #include